NoriginMedia / Norigin-Spatial-Navigation

React Hooks based Spatial Navigation (Key & Remote Control Navigation) / Web Browsers, Smart TVs and Connected TVs
MIT License
315 stars 94 forks source link

How to deal with conditional rendering of a FocusContext #127

Closed gsdev01 closed 6 months ago

gsdev01 commented 6 months ago

Hi, I was wondering the best solution to handle conditionnal rendering Focus Context.

for example :

// ...
    const { focusKey, focusSelf, ref } = useFocusable();
return (
  <>
    {visible && (
      <FocusContext.Provider value={focusKey}>
       {children}
      </FocusContext.Provider>
    )}
  </>
)

With this code, if visible is false FocusContext will not be rendered but the hook useFocusable will be called anyway resulting with a null node (but this null node is still focusable by the lib...). The work around right now we have to wrap the into its own component to get the hook called every time :

return ( 
  <>
    {visible && (
      <FocusContextWrapper /> // contains useFocusable hook call
    )}
  </>
)

Is there any other way to do this ? And also why a null node is still focusable ?

guilleccc commented 6 months ago

Hi @gsdev01 ,

Based on what you describe here, I believe the second code example looks right:

{visible && (
      <FocusContextWrapper /> 
    )}

Then the component FocusContextWrapper (and the internal hook useFocusable) will be rendered only if visible.

However, It would be nice to know a bit more context on the component containing the code above. What is actually the FocusContextWrapper doing?

I believe the another alternative you have here is to use the FocusContext but render its children conditionally:

const { focusKey, focusSelf, ref } = useFocusable();
return (
    <FocusContext.Provider value={focusKey}>
        {visible && children}
    </FocusContext.Provider>
)

This issue will be closed, but please feel free to reopen if you need more information

gsdev01 commented 6 months ago

Thanks for the help @guilleccc , I would like to add fews things to my previous post:

Based on what you describe here, I believe the second code example looks right:

{visible && (
      <FocusContextWrapper /> 
    )}

Then the component FocusContextWrapper (and the internal hook useFocusable) will be rendered only if visible.

However, It would be nice to know a bit more context on the component containing the code above. What is actually the FocusContextWrapper doing?

FocusContextWrapper is just the focusable component in an other component like so :

// FocusContextWrapper.tsx
// ...
const { focusKey, focusSelf, ref } = useFocusable();

return (
      <FocusContext.Provider value={focusKey}>
        <div ref={ref}>content</div>
      </FocusContext.Provider>
)

So the component and the hook will be called and destroyed everytime visible is true.

I believe the another alternative you have here is to use the FocusContext but render its children conditionally:

const { focusKey, focusSelf, ref } = useFocusable();
return (
    <FocusContext.Provider value={focusKey}>
        {visible && children}
    </FocusContext.Provider>
)

With this solution the hook is called even if children is not visible and the children should contain a ref like so:

// children
<div ref={ref}>content</div>

resulting with console warning : Component added without a node reference. This will result in its coordinates being empty and may cause lost focus. Check the "ref" passed to "useFocusable" and we have nullfor node field....

In addition, I'm using this conditional rendering for a Modal. So when the visible state become true, Modal component should gained the focus but how to handle the focus dispatch when visible become false ? Right now, the focus is lost... Do I have to set it manually ?

Thanks for the help

gsdev01 commented 6 months ago

Can't re-open this by myself btw @guilleccc