-
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
Docs: Clarification of setState() behavior #9329
Conversation
`setState()` is a frequent source of confusion for people new to React, and I believe part of that is due to minimization of the impact of the asynchronous behavior of `setState()` in the documentation. This revision is an attempt to clarify that behavior. For motivation and justification, see [setState Gate](https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82).
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.
Thanks for doing this @ericelliott, I think it's a step in the right direction.
``` | ||
|
||
Performs a shallow merge of nextState into current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks. | ||
`setState()` is an asynchronous method which enqueues changes to be made to the state during the next update cycle. This is the primary method you use to trigger UI updates in response to UI event handlers, server responses, etc... |
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 can probably get away with removing UI here
trigger updates in response to event handlers, server responses, etc...
``` | ||
|
||
It's also possible to pass a function with the signature `function(state, props) => newState`. This enqueues an atomic update that consults the previous value of state and props before setting any values. For instance, suppose we wanted to increment a value in state by `props.step`: | ||
`prevState` is a reference to the previous state. It should not be directly mutated. Instead, changes should be represented by building a `nextState` based on the input from `prevState` and `props`. For instance, suppose we wanted to increment a value in state by `props.step`: |
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.
building a
nextState
Maybe change to
build a new state object
As it's unclear what nextState
is if you're not already familiar with setState callback signature
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.
Good point. =)
You may optionally pass an object as the first argument to `setState()` instead of a function: | ||
|
||
```javascript | ||
setState(stateChange, callback) |
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 use an object literal instead so it's a little more familiar? Maybe use the same example above that updates myInteger
setState({ myInteger: this.state.myInteger + this.props.step })
That could also serve as an example of a situation where the setState callback is preferred, as this.state.myInteger
may not be totally up-to-date.
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 intended to show the signature, not an example, but you make a good point that an example would be helpful.
Here is the simple object usage: | ||
There is no guarantee of synchronous operation of calls to `setState`. | ||
|
||
`setState()` will always lead to a re-render unless `shouldComponentUpdate()` returns `false`. If mutable objects are being used and conditional rendering logic cannot be implemented in `shouldComponentUpdate()`, calling `setState()` only when the new state differs from the previous state will avoid unnecessary re-renders. |
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 part feels a little too opaque for beginners
If mutable objects are being used and conditional rendering logic cannot be implemented in
shouldComponentUpdate()
What does conditional rendering logic have to do with shouldComponentUpdate
and setState
?
calling
setState()
only when the new state differs from the previous state will avoid unnecessary re-renders.
Don't you only get the new state by calling setState
in the first place? I get what you mean, but it's slightly ambiguous.
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 didn't write this part. It's already in the docs. Maybe cleanup of that wording could happen in a separate PR.
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.
Ah, the diff made it look like you did. It would be nice to simplify this here or in another PR 👍
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 agree. I wasn't sure what to do with it, so I left it alone for now. =)
Other comments have been addressed. |
``` | ||
|
||
Performs a shallow merge of nextState into current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks. | ||
`setState()` is an asynchronous method which enqueues changes to be made to the state during the next update cycle. This is the primary method you use to trigger updates in response to event handlers, server responses, etc... |
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.
setState()
is an asynchronous method
Sometimes it's sync and sometimes it's async, depending on the priority of the update.
during the next update cycle
What does "update cycle" mean? The next time the component is rendered? That's not true because the update might not have enough priority.
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.
Suggestions for rewording?
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.
My suggestion:
setState()
enqueues changes to the component state and tells React to re-render this component and its children. This is the primary method you use to update the user interface in response to event handlers, server responses, etc.Think of
setState()
as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
What do you think?
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.
Love it.
|
||
The first argument can be an object (containing zero or more keys to update) or a function (of state and props) that returns an object containing keys to update. | ||
`setState()` does not immediately mutate `this.state` but creates a pending state transition. Accessing `this.state` after calling this method can potentially return the previous state, rather than the state after enqueued updates have been applied. This is a common source of bugs in React applications. |
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.
Accessing
this.state
after calling this method can potentially return the previous state
It could also return a future state that hasn't flushed yet, or a state that was aborted completely.
This is a common source of bugs in React applications.
Source? This seems overly alarmist.
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 know this from mentoring students & product teams who are making the switch to React. I saw two examples yesterday. See setState Gate. Further evidence is available by searching for setState on Stack Overflow (there are MANY more examples of confused setState users on Stack Overflow).
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.
potentially return the previous state
That bit is just a slight rephrase of the current doc, which says "potentially return the existing value".
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.
Maybe
setState()
does not immediately mutatethis.state
. Instead, React mutatesthis.state
when it applies the enqueued update, and this may happen later than thesetState()
call. This makes readingthis.state
right after callingsetState()
a potential pitfall.
?
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 better, but I'm concerned that it implies that this.state
is a source of truth. You can mostly get away with this assumption in sync mode, with the exception that you describe here. But this won't be the case with async.
What I'd like to write is
You should only read
this.state
(orthis.props
) inside a lifecycle method orrender
. It's not safe in an event handler, promise handler, timeout callback, or any place in the stack above React, where you should usethis.setState((state, props) => stateUpdate)
instead.
but this might be too prescriptive. Because again, this isn't really an issue in sync mode, only with async. And we'll likely have to come up with a new API for async mode, anyway, at which point this setState
doc will be moot.
Maybe this is a good way to split the difference:
setState()
does not always immediately update the component. It may batch or defer the update until later. This makes readingthis.state
right after callingsetState()
a potential pitfall. Instead, usecomponentDidUpdate
or asetState
callback (setState(update, callback)
), either of which are guaranteed to fire after the update has been applied.
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 appreciate the intention of clarifying how setState
works, but these changes are too focused on implementation details for my taste. I would prefer explanations that draw on design principles (see: this section on scheduling).
Implementation details can change (and will change significantly when we go async-by-default in React 17), but our design principles are fairly constant.
@acdlite Do you have any specific suggestions about how to improve this proposal? Surely we can improve on the current api doc. |
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.
Left a few comments with requested/suggested changes based on discussion. @ericelliott Would you like to amend to that?
``` | ||
|
||
Performs a shallow merge of nextState into current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks. | ||
`setState()` is an asynchronous method which enqueues changes to be made to the state during the next update cycle. This is the primary method you use to trigger updates in response to event handlers, server responses, etc... |
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.
My suggestion:
setState()
enqueues changes to the component state and tells React to re-render this component and its children. This is the primary method you use to update the user interface in response to event handlers, server responses, etc.Think of
setState()
as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
What do you think?
|
||
The first argument can be an object (containing zero or more keys to update) or a function (of state and props) that returns an object containing keys to update. | ||
`setState()` does not immediately mutate `this.state` but creates a pending state transition. Accessing `this.state` after calling this method can potentially return the previous state, rather than the state after enqueued updates have been applied. This is a common source of bugs in React applications. |
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.
Maybe
setState()
does not immediately mutatethis.state
. Instead, React mutatesthis.state
when it applies the enqueued update, and this may happen later than thesetState()
call. This makes readingthis.state
right after callingsetState()
a potential pitfall.
?
I took the liberty of updating the doc according to @acdlite’s comments and my understanding. @ericelliott I hope you won’t mind that I keep this credited to you even though I rewrote most parts of it. Your starting the conversation was very helpful, and I’m worried that we’ll keep bikeshedding here and won’t ever ship this. So I’d rather make an incremental improvement (largely based on your template, but addressing @acdlite’s comments) than keep beating this PR until everyone loses interest. Thank you! |
* Clarification of setState() behavior `setState()` is a frequent source of confusion for people new to React, and I believe part of that is due to minimization of the impact of the asynchronous behavior of `setState()` in the documentation. This revision is an attempt to clarify that behavior. For motivation and justification, see [setState Gate](https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82). * Update reference-react-component.md * Signature fix * Update to address @acdlite concerns * Add more details
Thanks! |
+100! |
* Clarification of setState() behavior `setState()` is a frequent source of confusion for people new to React, and I believe part of that is due to minimization of the impact of the asynchronous behavior of `setState()` in the documentation. This revision is an attempt to clarify that behavior. For motivation and justification, see [setState Gate](https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82). * Update reference-react-component.md * Signature fix * Update to address @acdlite concerns * Add more details
In order to ensure consistency between the 15-stable and 15.6 branches, we found all the diffs present only in 15-stable and cherry-picked them onto 15.6-dev. These are 100% documentation updates, which is why we didn't bother cherry-picking them in the first place, but having them on the 15.6-dev branch makes the two branches more consistent and that is better. Nobody will accidentally see outdated docs on the 15.6-dev branch these diffs won't somehow be lost or overwritten when we combine the branches to release 15.6. Test Plan: Manually tested the docs, looking at many of the updated pages. Also ran all tests etc. Changelog below: --- * Fix the proptypes deprecation warning url on the "Don't Call PropTypes Warning" doc page (#9419) * Use the same prop-types link on the warning docs page as the main proptypes doc page * Link to repo instead * Docs: Clarification of setState() behavior (#9329) * Clarification of setState() behavior `setState()` is a frequent source of confusion for people new to React, and I believe part of that is due to minimization of the impact of the asynchronous behavior of `setState()` in the documentation. This revision is an attempt to clarify that behavior. For motivation and justification, see [setState Gate](https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82). * Update reference-react-component.md * Signature fix * Update to address @acdlite concerns * Add more details * Update proptypes doc (#9391) * Update proptypes doc * Removed note * Add tabs to installation page (#9275, #9277) (#9401) * Add tabs to installation page (#9275, #9277) This adds tabs for create-react-app and existing apps to the installation section of the docs. The tab implementation is a simplified version of React Native's installation page. Fixes #9275. * Use classList instead of className * Use same implementation as in RN * Refractor docs to indicate that state set to props in constructor will not recieve the updated props (#9404) * Switch Installation to a tab when hash is present (#9422) * Updated the Good First Bug section in readme (#9429) * Updated the Good First Bug section in readme * Inconsistent use of quotes. Prefered single quotes instead of double quotes * Updated Good first bug link in how_to_contribute doc. * Undo JSX attribute quote change * don't capitalize "beginner friendly issue" (cherry picked from commit 36c935c) * [Documentation] Impreove the react-component section of doc (#9349) * Impreove react-component of doc [#9304](#9304) * update description * add missing space (cherry picked from commit 359f5d2) * Unique headings for linking purposes (#9259) Previously two headings were 'Javascript Expressions' - now 'Javascript Expressions as Props' and 'Javascript Expressions as Children' (cherry picked from commit 363f6cb) * Update jsx-in-depth.md (#9178) * Update jsx-in-depth.md Line 9 isn't changed * Move selection down * Fix (cherry picked from commit ccb38a9) * Sort out conferences by date (#9172) (cherry picked from commit 37f9e35) * Lift state up - Updating the documentation to mention that onClick is a synthetic event handler (#9427) * Lift state up - Updating the documentation to mention that onClick is a synthetic event handler * Review comments - Rephrase to handle synthetic events and event handler patterns * Tweak (cherry picked from commit 53a3939) * Fixed grammar (#9432) * Update codebase-overview.md * Some more fixes (cherry picked from commit d724115) * [Tutorial] ES6, installation, and button closing tag (#9441) * adds notes to tutorial on es6 and installation * fixes tutorial mention of opening button tag * More writing * Update * Minor tweaks to tutorial * Fix duplicate sentence * Minor tutorial nits * Add missing tutorial sidebar links * Tweak tutorial structure * FIX: Move CRA build info under it's tab page (#9452) * FIX: Move CRA build info under it's tab page * Add some links * Reorganize the "following along" instructions (#9453) * Reorganize the "following along" instructions * Minor tweaks (cherry picked from commit 1ce562e) * [Docs] Add accessibility to tabs in installation documentation (#9431) * Add accessibility to tabs in installation documentation * Change color and fix styling (cherry picked from commit 9526174) * [Docs: Installation] Fix tabs responsive layout - Resubmit (#9458) * [Docs: Installation] Fix tabs responsive layout * Move tabs a pixel down * Remove left margin on first tab * Remove the long line * Fix mobile styles (cherry picked from commit a92128e) * Add more details in jsx-in-depth.md (#9006) * jsx-in-depth.md add ternary statement for javascript expressions section * jsx-in-depth.md add explanation to get falsey values for props * update jsx-in-depth.md * ensure links work locally, remove section about falsey prop values * Fix links * Add link to 'Typechecking with PropTypes' under 'Advanced Guides' (#9472) This should have been retained in our docs, since PropTypes are only moved and not deprecated. Partially handles #9467, and I'll make a separate PR to https://github.com/reactjs/prop-types to add more docs to the README there. (cherry picked from commit 39ca8aa) * Updates how-to-contribute.md to use JSFiddle referenced in submit Git issue template (#9503) (cherry picked from commit c8a64e2) * Describe fixtures dir in the codebase overview (#9516) * Describe fixtures dir in overview * Fix folder name (cherry picked from commit d12c41c) * adds indirect refs to docs (#9528) * adds indirect refs to docs * Add more info * Explain clearer * Rephrase * Update refs-and-the-dom.md * Update refs-and-the-dom.md * Update refs-and-the-dom.md * Update refs-and-the-dom.md * Update refs-and-the-dom.md * Update refs-and-the-dom.md * Update refs-and-the-dom.md * Update refs-and-the-dom.md * Update refs-and-the-dom.md * Update refs-and-the-dom.md * Update refs-and-the-dom.md (cherry picked from commit 14fa8a5) * Add reference to the Hyperscript libraries (#9517) * Add reference to the Hyperscript libraries I feel these should be mentioned as they provide terser syntax than using `R.createElement` directly, even with a shorthand. * Rephrase (cherry picked from commit a8c223a) * [Docs] Fix confusing description for the <script>...</script> usage (#9502) * Fix confusing description for the <script>...</script> usage * Update jsx-in-depth.md * Update reference-react-dom-server.md * Update reference-react-dom.md * Update reference-react.md (cherry picked from commit 3d60d4c) * [site] Load libraries from unpkg (#9499) * [site] Load libraries from unpkg * Revert Gemfile changes (cherry picked from commit cf24d87) * fixed error formatting in live editor (#9497) (cherry picked from commit 7ccfb07) * React.createElement syntax (#9459) * React.createElement syntax Added React.createElement syntax. I think this is required for this tutorial. * Reword (cherry picked from commit 9824d52) * Add guide on integrating with non-react code (#9316) * Add guide on integrating with non-react code * Capitalize guide title * Make links to other docs relative * Rephrase 'What it does do' * Remove experimental syntax * Capitalize Backbone * Remove empty lifecycle method in generic jQuery example * Use shouldComponentUpdate() not componentWillUpdate() * Prefer single quotes * Add cleanup to generic jQuery example * Capitalize React * Generalize the section on Backbone Views * Generalize the section on Backbone Models, a little * Add introduction * Adjust wording * Simplify ref callbacks * Fix typo in generic jQuery example * Fix typos in Backbone models in React components * Fix more typos in Backbone models in React components * Add generic section on integrating with other view libraries * Stress the benefits of an unchanging React element * Small changes to introduction * Add missing semicolon * Revise generic jQuery wrapper section Moved the section on using empty elements to prevent conflicts above the code example and added brief introduction to that example. * Add usage example for Chosen wrapper * Prevent Chosen wrapper from updating * Note that sharing the DOM with plugins is not recommended * Mention how React is used at Facebook * Mention React event system in template rendering section * Remove destructuring from function parameters * Do not name React components Component * Elaborate on unmountComponentAtNode() * Mention preference for unidirectional data flow * Rename backboneModelAdapter * Replace rest syntax * Respond to updated model in connectToBackboneModel * Rewrite connectToBackboneModel example * Rework connectToBackboneModel example * Misc changes * Misc changes * Change wording * Tweak some parts (cherry picked from commit 1816d06) * Don't build gh-pages branch on CircleCI (#9442)
* Clarification of setState() behavior `setState()` is a frequent source of confusion for people new to React, and I believe part of that is due to minimization of the impact of the asynchronous behavior of `setState()` in the documentation. This revision is an attempt to clarify that behavior. For motivation and justification, see [setState Gate](https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82). * Update reference-react-component.md * Signature fix * Update to address @acdlite concerns * Add more details
setState()
is a frequent source of confusion for people new to React, and I believe part of that is due to minimization of the impact of the asynchronous behavior ofsetState()
in the documentation. This revision is an attempt to clarify that behavior. For motivation and justification, see setState Gate.