DEV Community

loading...
Cover image for react hooks - useState, useEffect

react hooks - useState, useEffect

ajcwebdev profile image anthonyCampolo ・3 min read

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 familiar componentDidMount and componentWillUnmount 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

pic
Editor guide