-
Notifications
You must be signed in to change notification settings - Fork 424
Conversation
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.
This looks good in principle – you will need to update the React test snapshot (use the -u
flag). We already have logic that applies keys in branching.js
but I like your approach here in use fragments. We do create more unnecessary nodes that required, but given they're fragments, it shouldn't be a big deal.
src/react/reconcilation.js
Outdated
// If we have a new result and we might have a key value then wrap our inlined result in a | ||
// `<React.Fragment key={keyValue}>` so that we may maintain the key. | ||
if (needsKey && keyValue.mightNotBeNull()) { | ||
const react = this.realm.fbLibraries.react; |
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.
Can we extract this key adding logic into its own function that has a descriptive name. We might want to apply the same logic in other parts of the reconciler or in branching.js
at some point so it would be useful to have as utility function.
Keys shouldn't matter for first render mode, but matter in normal mode. |
@gaearon could they matter when we hydrate the first-render tree? For instance if we bail out on a class component. Or is my mental model of hydration wrong? |
@calebmer Hydration isn't affected by keys. Furthermore, we never render any key information or metadata during server-side render, so the hydration process always mounts components when traversing the SSR tree. |
…sable for first render mode
@gaearon disabled this for first render mode 👍 |
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.
calebmer is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
I don't quite understand the discussion about hydration in this thread. SSR works in two steps:
An ideal end state is Prepack being able to produce two bundles. One for the initial render, and the other one being "just enough to support updates without repeating the initial render logic". However this would require React to have a way to "progressively enhance" components with update logic after they've already rendered or been hydrated. Some future things we talked about can help us with that. I'm not sure how we'd deal with keys in this scenario but they would probably have to be specified in the initial render bundle so that the first update is hooked up correctly. So maybe we're really talking about three bundles: one for SSR (no keys), one for first render (with keys), and one for updates. But that's a far future ideal, and not what we do today. |
Fixes #2322. The React Compiler was discarding keys for components that were inlined.
Consider:
Input
Output
Cases where this can be an issue:
Of course, it’s possible this may be a non-issue.
My solution for this was to wrap the inlined React Element in a
<React.Fragment>
with a key. So my input above becomes:Cloning inlined elements and adding keys is a fragile operation since the element may already have a key or the element may be some other React node like a portal. I opted for wrapping in
<React.Fragment>
. Note that this also means we can hoist<div />
in the example above.Thoughts on adding an optimization which clones the element and adds
key={x}
in cases where this is possible?<React.Fragment>
seems correct in all cases, but cloning withkey={x}
when possible seems like it might be faster.Also happy to hear other suggestions for maintaining the key on inlined elements.
This has no impact on our internal bundle.