-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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
Allow multiple root children in test renderer traversal API #13017
Conversation
Details of bundled changes.Comparing: d480782...c99ae2e react-test-renderer
Generated by 🚫 dangerJS |
What's the issue describing the problem this fixes? |
There's no particular issue report but you can try only cherry-picking the new test and apply it on master. You'll see both invariants (in some cases) and mismatches (in other cases). |
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.
]); | ||
|
||
function getChildren(parent: Fiber) { |
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.
Nit - we could explicitly flow type the return value; Array<ReactTestInstance | string>
.
Also I see that this was copy-pasted from the getChildren
method below. Wish there was a docblock, it's not very clear to me why this works the way it does. But that could be for the future.
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.
We don't always Flow-type internal functions because those change more often. I figured that since we still have an outer annotation on the public API, we can leave this one inferred. If it returns something bad the annotation on the calling method should catch it.
if (node.child === null) { | ||
return children; | ||
} | ||
node.child.return = node; |
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.
Granted this is copy-pasted - but wondering if you know, why do we mutate the fiber instance to set it's return here?
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.
So, this is a pattern we use everywhere we traverse fibers deeply. It's necessary because each fiber has two copies. The return
pointer points to the parent but we don't know which copy (current of work in progress). If we just naïvely follow "return" to go up, we might end up in a different tree copy from the previous update.
This is why every time we descend down, no matter when, we always overwrite the .return
pointer to the parent in the tree we're working on. This should have no observable side effect because we do this on every deep traversal, and we never trust the chain of return
pointers to give us the "current" tree.
} | ||
while (node.sibling === null) { | ||
if (node.return === startingNode) { | ||
break outer; |
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.
} | ||
(node.sibling: any).return = node.return; | ||
node = (node.sibling: any); | ||
} |
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.
I'm sure it will become clear as I continue reading, but I was surprised that this method mutates the nodes and goes down to potentially get deeply nested children.
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 is just copy pasted from below. I didn't write it.
It works this way to skip over fragments / context consumers / other wrappers in the tree, and only "see" custom components and host nodes in traversal APIs.
My idea was that .root would give you a node that can be unmounted if it's swapped out, whereas the test renderer itself remains the same object no matter what updates you do. I guess this is consistent with that. |
Yeah I think this is still true. The case where you first render fragment with one child and then render it with multiple children might be a bit confusing because |
#7516 was where that was discussed I guess. But I think we got rid of that restriction. |
renderer.update(<Fragment><div /></Fragment>).root // div
renderer.update(<Fragment><div /><div /></Fragment>).root // rooty mcrootface -- is that weird? |
I think it's OK. |
Great, thanks for the fix! |
193: Update dependency react to v16.4.1 r=magopian a=renovate[bot] This Pull Request updates dependency [react](https://github.com/facebook/react) from `v16.4.0` to `v16.4.1` <details> <summary>Release Notes</summary> ### [`v16.4.1`](https://github.com/facebook/react/blob/master/CHANGELOG.md#​1641-June-13-2018) [Compare Source](facebook/react@v16.4.0...v16.4.1) ##### React * You can now assign `propTypes` to components returned by `React.ForwardRef`. ([@​bvaughn] in [#​12911](`https://github.com/facebook/react/pull/12911`)) ##### React DOM * Fix a crash when the input `type` changes from some other types to `text`. ([@​spirosikmd] in [#​12135](`https://github.com/facebook/react/pull/12135`)) * Fix a crash in IE11 when restoring focus to an SVG element. ([@​ThaddeusJiang] in [#​12996](`https://github.com/facebook/react/pull/12996`)) * Fix a range input not updating in some cases. ([@​Illu] in [#​12939](`https://github.com/facebook/react/pull/12939`)) * Fix input validation triggering unnecessarily in Firefox. ([@​nhunzaker] in [#​12925](`https://github.com/facebook/react/pull/12925`)) * Fix an incorrect `event.target` value for the `onChange` event in IE9. ([@​nhunzaker] in [#​12976](`https://github.com/facebook/react/pull/12976`)) * Fix a false positive error when returning an empty `<React.Fragment />` from a component. ([@​philipp-spiess] in [#​12966](`https://github.com/facebook/react/pull/12966`)) ##### React DOM Server * Fix an incorrect value being provided by new context API. ([@​ericsoderberghp] in [#​12985](`https://github.com/facebook/react/pull/12985`), [@​gaearon] in [#​13019](`https://github.com/facebook/react/pull/13019`)) ##### React Test Renderer * Allow multiple root children in test renderer traversal API. ([@​gaearon] in [#​13017](`https://github.com/facebook/react/pull/13017`)) * Fix `getDerivedStateFromProps()` in the shallow renderer to not discard the pending state. ([@​fatfisz] in [#​13030](`https://github.com/facebook/react/pull/13030`)) --- </details> --- This PR has been generated by [Renovate Bot](https://renovatebot.com). Co-authored-by: Renovate Bot <[email protected]>
25: Update react monorepo to v16.4.1 r=renovate[bot] a=renovate[bot] This Pull Request renovates the package group "react monorepo". - [react-dom](https://github.com/facebook/react) (`dependencies`): from `16.4.0` to `16.4.1` - [react](https://github.com/facebook/react) (`dependencies`): from `16.4.0` to `16.4.1` # Release Notes <details> <summary>facebook/react</summary> ### [`v16.4.1`](https://github.com/facebook/react/blob/master/CHANGELOG.md#​1641-June-13-2018) [Compare Source](facebook/react@v16.4.0...v16.4.1) ##### React * You can now assign `propTypes` to components returned by `React.ForwardRef`. ([@​bvaughn] in [#​12911](`https://github.com/facebook/react/pull/12911`)) ##### React DOM * Fix a crash when the input `type` changes from some other types to `text`. ([@​spirosikmd] in [#​12135](`https://github.com/facebook/react/pull/12135`)) * Fix a crash in IE11 when restoring focus to an SVG element. ([@​ThaddeusJiang] in [#​12996](`https://github.com/facebook/react/pull/12996`)) * Fix a range input not updating in some cases. ([@​Illu] in [#​12939](`https://github.com/facebook/react/pull/12939`)) * Fix input validation triggering unnecessarily in Firefox. ([@​nhunzaker] in [#​12925](`https://github.com/facebook/react/pull/12925`)) * Fix an incorrect `event.target` value for the `onChange` event in IE9. ([@​nhunzaker] in [#​12976](`https://github.com/facebook/react/pull/12976`)) * Fix a false positive error when returning an empty `<React.Fragment />` from a component. ([@​philipp-spiess] in [#​12966](`https://github.com/facebook/react/pull/12966`)) ##### React DOM Server * Fix an incorrect value being provided by new context API. ([@​ericsoderberghp] in [#​12985](`https://github.com/facebook/react/pull/12985`), [@​gaearon] in [#​13019](`https://github.com/facebook/react/pull/13019`)) ##### React Test Renderer * Allow multiple root children in test renderer traversal API. ([@​gaearon] in [#​13017](`https://github.com/facebook/react/pull/13017`)) * Fix `getDerivedStateFromProps()` in the shallow renderer to not discard the pending state. ([@​fatfisz] in [#​13030](`https://github.com/facebook/react/pull/13030`)) --- </details> --- This PR has been generated by [Renovate Bot](https://renovatebot.com). Co-authored-by: Renovate Bot <[email protected]>
This should fix some bugs @sahrens was seeing.
Test renderer traversal API currently treats "fragmenty" nodes (fragments, providers, modes) as passthrough. That usually works, but it breaks down at the top level. The
.root
node has to point to a Fiber, so if it's a "passthrough" Fiber, we currently invariant.Ideally I think the root node should have always been a special kind of "root" whose
type
andprops
don't match the nearest "materialized" node. But that ship has sailed, and changing this would be breaking.So for now I want to enable those use cases by only "materializing" a special root node (with
type: null, props: null
) when it has multiple children.It should be mostly additive because that kind of code used to throw. It does change the behavior for a top-level non-keyed fragment with many children (we used to ignore all children but the first) but this actually sounds like a bugfix to me, especially given that subsequent children were not ignored in the
toJSON()
API.In the future, we probably should change
.root
to always be a special node, regardless of whether it has one or more children.