-
Notifications
You must be signed in to change notification settings - Fork 47k
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
Fix #6114 - Calling setState inside getChildContext should warn #6121
Conversation
@@ -498,6 +498,7 @@ var ReactCompositeComponentMixin = { | |||
var inst = this._instance; | |||
var childContext = inst.getChildContext && inst.getChildContext(); | |||
if (childContext) { | |||
ReactCurrentOwner.processingChildContext = true; |
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.
Probably don't want to attach this to ReactCurrentOwner, since it has nothing to do with owners. But conceptually, yes, we want to set some sort of value when we start a getChildContext
.
I think it probably makes sense to use the new devtools api here. You can emit an event when you start/end getting the child context, and keep the state in a devtool instead of tying it into the core. Devtool api details: https://github.com/facebook/react/pull/5590/files
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.
Are there any examples of the Devtool api used in React code?
Would I be writing my own devtool function or do I use something like ReactDOMInstrumentation.debugTool.onSetValueForProperty(inst, 'processingChildContext', true)
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.
Would I be writing my own devtool function or do I use something like ReactDOMInstrumentation.debugTool.onSetValueForProperty(inst, 'processingChildContext', true)
I think both. Here’s another example of adding a devtool for warnings: 251d6c3
onSetValueForProperty()
will not be relevant here, it’s only called this way because it is called when React sets a value for DOM property.
In your case, you can add onBeginProcessingChildContext()
and onEndProcessingChildContext()
, and a new ReactInvalidSetStateWarningDevTool
(or something like this) that would set the flag.
Also, since it’s related to React
and not ReactDOM
in particular, perhaps it makes sense to use ReactInstrumentation
introduced in #6068 instead of ReactDOMInstrumentation
.
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.
Cool , Thanks!
So I notice I can't extend the internalInstance object to add the flag (that we are processingChildContext). What is a good object to add the flag to?
@raineroviir updated the pull request. |
@@ -48,6 +48,12 @@ var ReactDebugTool = { | |||
} | |||
} | |||
}, | |||
onBeginProcessingChildContext(instance, boolean) { |
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.
boolean
is a type. A good variable name would be something like isPprocessingChildContext
. But why does this variable exist at all? Isn't it always true in this function and always false in the other?
@raineroviir updated the pull request. |
1 similar comment
@raineroviir updated the pull request. |
if (processingChildContext === false) { | ||
return; | ||
} | ||
warning( |
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.
It might be more compact to write
warning(
!processingChildContext,
'setState(...): Cannot call setState inside getChildContext()'
);
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.
You're right, Thanks
This looks great to me! Let’s hear what @jimfb has to say. |
I am failing a test locally not sure why it isn't failing in travis. Is that kinda worrying?
|
@raineroviir updated the pull request. |
@@ -10,7 +10,7 @@ | |||
*/ | |||
|
|||
'use strict'; | |||
|
|||
var ReactInvalidSetStateWarningDevTool = require('./devtools/ReactInvalidSetStateWarningDevTool'); |
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.
Normally we would just do require('ReactInvalidSetStateWarningDevTool')
, so this seems weird. I'm assuming you did this because require('ReactInvalidSetStateWarningDevTool')
didn't work for you because you didn't have an @providesModule
at the top of that file.
If you add the standard copyright header to ReactInvalidSetStateWarningDevTool.js
and specify the @providesModule
, we should be able to fix this line.
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.
Got it. That providesModule thing is super cool
I looked into the failing unit test, it looks like a valid failure caused by this change (ie. needs to be fixed before we can merge). That test is not invoking I'm not sure why it didn't fail travis (cc @zpao). |
@raineroviir updated the pull request. |
@raineroviir updated the pull request. |
@@ -783,7 +812,7 @@ describe('ReactCompositeComponent', function() { | |||
|
|||
render: function() { | |||
var output = <Child />; | |||
if (!this.state.flag) { | |||
if (this.state.flag) { |
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.
What is going on here? Why this change?
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 wanted to see why the test was throwing the warning
- in the original test we rendered
<Parent> <Child /> <Parent>
, and onsetState
we change the render to<Parent><span>Child</span></Parent>
- in the changed test which doesn't create a warning we go from
<P><span>Child</span> </P>
to<P><Child /><P>
I commited the change here for now in case it could benefit our search for the fix
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.
Ok, well, we still need to fix so the original test passes.
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! I will make the original test pass
Ping @raineroviir |
I've tried many things I'm not sure how to go about fixing the test. |
Wow I spent so long on this problem. I looked at some awesome code that vslinko wrote to fix this issue and it was so simple, I just did not fully understand the issue I was fixing. I made an early assumption of where the code should go and tried everything to make it work. Sigh. Like trying to stick a square peg in a round hole |
@raineroviir updated the pull request. |
expect(instance.state.value).toBe(1); | ||
expect(console.error.calls.length).toBe(1); | ||
expect(console.error.argsForCall[0][0]).toBe( | ||
'Warning: setState(...): Cannot call setState inside getChildContext()' |
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.
Nitpicking: let’s add ()
for consistency? As in Cannot call setState() inside getChildContext()
.
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.
Sure
The earlier problem was likely due to Great job! Thank you for following through with this. |
Would you mind squashing it as a single commit? |
29f8ff4
to
3f2ffeb
Compare
@raineroviir updated the pull request. |
@raineroviir updated the pull request. |
@raineroviir updated the pull request. |
I totally just squashed all those commits! Thanks for all the great comments guys, no better way to learn how to do this stuff than by doing |
This looks great to me, thanks @raineroviir! |
Fix #6114 - Calling setState inside getChildContext should warn
It doesn't pass one of the tests, but I'm curious if this is the right direction or not.