useState
Returns a stateful value, and a function to update it.
- During initial render, returned state (
state
) is the same as the value passed as first argument (initialState
) -
setState
function is used to update state- Accepts a new state value
- Enqueues a re-render of the component
Functional updates
If the new state is computed using the previous state, you can pass a function to setState
.
The function will receive the previous value, and return an updated value. This counter component uses both forms of setState
:
- "+" and "-" use the functional form, because the updated value is based on the previous value.
- "Reset" uses the normal form, because it always sets the count back to the initial value.
Lazy initial state
The initialState
argument is the state used during the initial render.
- It is disregarded in subsequent renders
- Can be used if the initial state is the result of an expensive computation.
Bailing out of a state update
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. React may still need to render that specific component again before bailing out. If you're doing expensive calculations while rendering, you can optimize them with useMemo
.
useEffect
Accepts a function that contains imperative, possibly effectful code.
Side effects not allowed inside the main body of a function component (React's render phase):
- Mutations
- Subscriptions
- Timers
- Logging
The function passed to useEffect
will run after the render is committed to the screen. By default, effects run after every completed render, but you can choose to fire them only when certain values have changed.
Cleaning up an effect
Often, effects create resources that need to be cleaned up before the component leaves the screen, such as a subscription or timer ID. To do this, the function passed to useEffect
may return a clean-up function. For example, to create a subscription:
The clean-up function runs before the component is removed from the UI to prevent memory leaks. If a component renders multiple times (as they typically do), the previous effect is cleaned up before executing the next effect, meaning a new subscription is created on every update.
Timing of effects
Unlike componentDidMount
and componentDidUpdate
, the function passed to useEffect
fires after layout and paint, during a deferred event. However, not all effects can be deferred. For these types of effects, React provides useLayoutEffect
.
It has the same signature as useEffect
, and only differs in when it is fired. Although useEffect
is deferred until after the browser has painted, it's guaranteed to fire before any new renders. React will always flush a previous render's effects before starting a new update.
Conditionally firing an effect
Effects fire after every completed render to ensure they are always recreated if its dependencies changes. But in some cases, like the subscription example, we only need a new subscription if the source
prop has changed.
To implement this, pass a second argument to useEffect
that is the array of values that the effect depends on.
Now the subscription will only be recreated when props.source
changes.
Make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders.
If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]
) as a second argument.
- This tells React that your effect doesn't depend on any values from props or state, so it never needs to re-run.
- This isn't handled as a special case -- it follows directly from how the dependencies array always works.
If you pass an empty array ([]
), the props and state inside the effect will always have their initial values.
- While passing
[]
as the second argument is closer to the familiarcomponentDidMount
andcomponentWillUnmount
mental model, there are usually better solutions to avoid re-running effects too often. - React defers running
useEffect
until after the browser has painted, so doing extra work is less of a problem.
The array of dependencies is not passed as arguments to the effect function.
- Conceptually, though, that's what they represent
- Every value referenced inside the effect function should also appear in the dependencies array
Discussion