-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TreeItem] No conditional rendering support #19454
Comments
This is an easy fix but due to the massive changes in #18357 I want to wait for that to be merged. |
To touch on this, children already register with context but to populate their children they use React.Children. You could potentially use context fully but you would have to create a provider per item with children and I'm unsure about the performance issues that might cause. |
@joshwooding Tricky problem, at least with the current error, users know upfront it's not supported. |
@oliviertassinari For now adding defensive logic should provide an easy fix. I’m going to do some investigation on using multiple contexts instead but it doesn’t feel like an amazing avenue to me. |
I'm not sure to follow. If the introspection of the children is required, would a defensive logic hide the problem, without solving it? By hide, I meant, it will be harder for user to figure out what's wrong with their code. By note solving, I meant, the component won't behave as expected (even without the throw). |
If I read it correctly, defensive logic would just mean checking to see if the child is truthy before accessing properties of it. I'd also be interested in a performance evaluation of per-child Providers. I wouldn't naively anticipate it being much greater impact than maybe causing an additional render per item, which while not amazing, shouldn't be so bad for such minimal DOM structures which most likely won't actually change (since the context is only used for internal book-keeping, so React will just no-op the DOM changes). But that's only a guess. |
Thinking about this more, context would only solve the problem of a node knowing it's parent not necessarily knowing it's position amongst it's siblings... |
Isn't it the parent's job to know the order of siblings? A node should be able to say "go to the next tree item" without knowing which tree item that is, and let the parent determine where to move focus. const { isActive, goToNext, goToPrevious, register } = useTreeItem(nodeId);
const [element, setElement] = useState(null);
const elementRef = el => setElement(el);
useEffect(() => {
register(nodeId, element);
}, [element]);
const onKeyDown = ev => {
if (ev.key === 'ArrowDown') {
goToNext();
} else if (ev.key === 'ArrowUp') {
goToPrevious();
}
}
return <Component ref={elementRef} onKeyDown={onKeyDown} isActive={isActive} {/* etc */} />; I admit I'm not very familiar with the existing implementation details. |
That's pretty much how to current implementation works. The problem is that the parent only knows the order of the children by looking at the dom structure, (which might be nested) or the component order. I haven't figured out a great way to not depend on React.Children when doing this. |
I see. That is a tough one. Examining the nested child structure sorta seems like the most stable approach to me. Recomputing the child order would only have to happen when a child registers/unregisters, not every render. In fact, now that I'm thinking about it, I've actually done this before. The project was closed-source, so I'll just post the relevant stuff in a gist... https://gist.github.com/gforrest-bw/d84a04dbcd4f07cf25db77f200fe5295 This implementation was for an Autocomplete/Suggestions system (before MUI added theirs to Labs), but the concept should be generalizable. The basic idea is to attach a It may not be totally refined, but it did work. This whole system may be useful enough that I should extract it into a library... I may do that... Obviously there are scaling issues as the child DOM gets more complex, but realistically these systems tend to happen near the bottom of the tree. People don't put keyboard-interactive focus managers at the root of their App... hopefully... Anyways, I think the practical solution today would just be to defend against |
Current Behavior 😯
TreeView
andTreeItem
throw an error when attempting to conditionally render aTreeItem
.Expected Behavior 🤔
Any
TreeItem
which is excluded from rendering by a condition (i.e. replaced withnull
) should not be rendered, remove itself from the tree structure, and no error is thrown.Steps to Reproduce 🕹
https://codesandbox.io/s/zen-breeze-fh11u
Steps:
<TreeView>
with child<TreeItem>
components<TreeItem>
in a conditional rendering expression ({!!condition && <TreeItem />}
)TreeItem
throws an error (it could be anotherTreeItem
or the topTreeView
):Cannot read property 'props' of null
Context 🔦
I'm using TreeView to render a navigation structure in a mobile nav. Depending on the roles and permissions of the user, certain pages are not available in the navigation structure. I therefore have to conditionally render certain tree items based on user information.
From the code it looks like the tree components are assuming that
React.Children.map
iterables will all be defined, but for conditionally rendered elements the values in the Children list will be null.Workaround
A workaround is present, but because of the way TreeView works at the moment it's slightly ugly.
You can define a simple wrapper component to handle conditional rendering:
You can then use it instead of traditional conditional rendering to avoid the error:
However, as shown above,
nodeId
is required for both the wrapper and the TreeItem, because TreeView/TreeItem iterates usingReact.Children
naively, assuming all children will beTreeItem
components withnodeId
props.That problem is not necessarily related to this issue, though. If there's interest I would open another issue with thoughts about the use of
React.Children
for this kind of thing (in short, I think children registering via Context is always a more flexible and stable option than reading data from children).Your Environment 🌎
The text was updated successfully, but these errors were encountered: