-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Pure render for tree data, fixes React Canary useMemo/useState StrictMode #5835
Conversation
Build successful! 🎉 |
if (!node) { | ||
return items; | ||
return {items, mappy: originalMap}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lmao mappy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change looks good!
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
let initialNodes = useMemo(() => buildTree(initialItems), []); | ||
let [items, setItems] = useState(initialNodes); | ||
let [tree, setItems] = useState<{items: TreeNode<T>[], mappy: Map<Key, TreeNode<T>>}>(() => buildTree(initialItems)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is it called mappy
? 😂
setItems(items => { | ||
let nodes = buildTree(values, parentKey); | ||
setItems(({items, mappy: originalMap}) => { | ||
let {items: nodes, mappy: newMap} = buildTree(values, parentKey, originalMap); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
won't this still result in originalMap being mutated? buildTree
only creates a new map if acc
is not provided, otherwise it reuses it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not quite sure I follow here. We are passing originalMap here to buildTree to build the newMap for the parentKey === null
case. If we didn't create pass it, buildTree would create a new map that would then only have the new nodes that are being inserted here instead of the full modified tree map since values
is just the new values being inserted
modifying the originalMap here shouldn't matter right since we are now storing the map in state and accessing the previous map inside the setState call?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah here https://github.com/adobe/react-spectrum/pull/5835/files#diff-c1a25c31946ec151047ebc265b0f5cf0971d990a3407c0dd66b7beecb1be4b24R151 we mutate the map that gets passed into buildTree
, thereby mutating the map stored in state.
Build successful! 🎉 |
Build successful! 🎉 |
this avoids the case where we may inadvertantly modify the previous map directly which could be problematic in a double render
Build successful! 🎉 |
Build successful! 🎉 |
## API Changes
unknown top level export { type: 'any' } |
Closes
Fixes test failures on main
The underlying root cause was that React made a PR which turned on double invocation for useState in React StrictMode, and it changed the value that useMemo returns to discard the second result of the run instead of the first.
This was a bug for us to fix because if we were a pure render then it should not matter which result they kept, it'd be the same. This puts our map into state so that we aren't mutating it anymore.
See facebook/react#28249 (comment)
✅ Pull Request Checklist:
📝 Test Instructions:
Smoketest both Canary and 18
🧢 Your Project: