-
Notifications
You must be signed in to change notification settings - Fork 47.5k
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
Update shouldComponentUpdate docs with advice about closures #12185
Comments
@thysultan That's the case of class Indirection extends React.Component {
shouldComponentUpdate() {
return false;
}
render() {
return (
<DataConsumer>
{nestedController => (
<div>
</div>
)}
</DataConsumer>
);
}
} |
@TrySound My assumption that this is a bug is based on this test case https://github.com/facebook/react/blob/master/packages/react-reconciler/src/tests/ReactNewContext-test.internal.js#L72-L138. where updates propagate through a |
@thysultan It's not a bug. Indirection component is not rerendered when any prop is changed. Consider |
@TrySound I'm not sure i understand, |
I think the test you linked to specifically confirms that the behavior works correctly. (The In this case, your test shows that changes to |
The behavior works for the first click, but subsequent clicks are not registered(every click should alternate between true and false). |
I don't know. This test is hard for me to read. There's several levels of indirection and stuff. I've tried to simplify it a little (so I can better understand it) here: I see the behavior I would expect from this. |
The expected result based on what i understand from the test specifications would be identical to https://codesandbox.io/s/321kq30kx1. |
Please take a look at the CodeSandbox I linked, and let me know what's unexpected. Your CodeSandbox is a bit odd to me. It has the following structure:
I'm not sure this level of nesting is indicative of a valid use-case. |
The CodeSandbox you linked to doesn't reveal the same behavior/bug. Yes there's a lot of Does the new Context API have restrictions on the amount of indirection a context update can propagate through? |
I'm just not convinced it is a reasonable bug. What does it even mean for a context's |
Can you point me to the example by Kent that you're referring to? |
I assume you're referring to this blog post? It does not demonstrate the odd wrapping/nesting behavior your CodeSandbox does. I think this is kind of a version of Kent's that shows the indirection you mention (and it also seems to work): |
The CodeSandBox is a fork of Kent's CodeSandBox – my fork added the |
Can you acknowledge the strange nesting structure and explain why it's important? I believe it's the cause of the behavior you're reporting as unexpected, and I don't understand why it's necessary. For instance, if you remove the double layer of |
@bvaughn a "Consumer wrapping its own Provider" is a pattern I ran into migrating code to the new API, as a way for having components map/alter context as it passes through. It does seem though like the problem is not that but |
To be clear, context supports nesting the same provider multiple times, and there are tests for this, so it's okay for a provider to be an ancestor of a consumer. The code snippet provided above just looked a bit unnecessarily/arbitrarily complex and so I was trying to better understand why it had to be that way. I'm not sure yet what the specific bit that's causing a problem is. Maybe I'll try to pair it down in a unit test this morning. |
In any case, whether there is a bug in React and whether the code is strange are two different discussions. |
sure, just trying to help out i've also not been able to simply the example effectively :/ |
Yeah, I just mean that
doesn't mean we shouldn't fix it. 🙂 |
I didn't mean to imply otherwise, although re-reading the comment you quoted above, I can see where it gave that impression. |
I thought so, just wanted to make sure the original author also knows this. 🙃 |
Good call. I'm going to pick this up now and see if I can catch it in a local test. (This was my intent anyway, I just wanted to eat breakfast first) 🤪 |
Here's a reduced case. https://codesandbox.io/s/k967n8x5v I'm not convinced it's a bug. I think it might be an artifact of reading |
Here's a more reduced case: https://codesandbox.io/s/mq29590rjy If you remove the I was thinking along the lines of what you're saying though, wrt stale data. |
Apparently I gave a wrong link :-( I had a smaller example but CodeSandbox didn't save it. |
Adding logging to the click handler (same sandbox) shows that it is calling
|
Here's an example with components combined https://codesandbox.io/s/kyrj9jq93 |
I think the problem is that the inner |
The consumer is being re-rendered each time though (as my added logging shows) |
That makes sense to me (and sounds as expected behavior). The tree is:
The fix would be to move indirection above the consumer. There is no way React could know that the leaf component (button) uses the value from above. |
Yup, yup. Agreed. |
I guess it's worth documenting as a gotcha. |
The original example still confuses me though. There, Indirection appears to be inside of the consumer.. |
Ahh I get it now. It's inside a nested consumer. It's trying to read a value from the top-level consumer though. This is exactly why "toggle nested" keeps working. It only uses context values "below" the indirection. |
Yup! |
This is why I hate render props 😂 Well, not "hate". But I do find that they present their own pitfalls compared to HOCs, and people too readily ignore them. Anyway, I'm fine with the new context API. Just ranting a bit. |
😆 cc @acdlite too (who is writing the new context API docs) |
There are a few things going on here. One is that closures are confusing. It's really easy to accidentally depend on values from an outer scope without realizing it. @gaearon is right that render props, because they rely on closures, can exacerbate this confusion. But in my view the core problem with all these examples is that By returning return false from Indirection's The unit tests for context happen always return false in I believe the actionable items for React here are:
|
From my understanding of the new API this looked like a bug since it manifested the same limitation of the old context API – that is i expected |
Maybe a simpler way to explain this is imagine you had code like this: <ContextConsumer>
{value => (
<Indirection> {/* always returns false from shouldComponentUpdate */}
{value}
</Indirection>
)}
</ContextConsumer> I hope you can see that there's no way It wouldn't even update if there was no context usage at all: function MyLabel(props) {
return (
<Indirection> {/* always returns false from shouldComponentUpdate */}
{props.value}
</Indirection>
);
} Does this make more sense now? New context doesn't force every single component below to update, just in case some of those components happen to pass that context value down and later render it. New context just fixes an issue where context consumers wouldn't get updated. From them and down, everything flow like normal React, subject to normal React rules. Another way to explain this is that your example would work if you changed |
@thysultan An easy way to confirm that the consumer re-renders is to put a log statement inside its render prop. |
I'm assuming that if it re-renders then it also reconciles the difference...
That is to say, it calls the render prop function and reconciles that part of the tree using the new return value.
The |
It applies in your case, it's just not very obvious why. The root of the issue is you're referencing top-level You'd have the exact same problem if you just closed over some variable at the top of the render method, and then have that "blocking" indirection in the middle. Even if a component deeper than the indirection re-rendered, it would still have the "stale" top-level value from a stale closure. |
Thanks, that makes sense. |
For anyone that wants to play around with this concept, I made a sandbox that might be useful. |
Lets me |
Closing since it doesn't seem to come up often. |
Do you want to request a feature or report a bug?
Possible bug depending on my understanding of the new context feature.
What is the current behavior?
The presence of an
<Indirection>
component withshouldComponentUpdate:false
is blocking updates to context consumers below it after the first click of "Toggle Top Data": https://codesandbox.io/s/v87rp187r7What is the expected behavior?
For consumers below a shouldComponentUpdate component to continue updating after the first "Toggle Top Data" click.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 16.3.0-alpha.0
The text was updated successfully, but these errors were encountered: