2 min read October 7, 2022

Derived state in React

Don't manage state, derive it

Derived state in React
Photo by Josh Withers on Unsplash

What is a derived state in React?

A derived state is a value that is computed from a state value. For example:

import React from 'react'

const Example: React.FC = () => {
  const [counter, setCounter] = useState(0)
  const doubleCounter = counter \* 2

  return <p>{doubleCounter}</p>
}

This might be a trivial example, but I’ve seen it used in a totally wrong way many times in React codebases.

import React, { useEffect } from 'react'

const Example: React.FC = () => {
  const [counter, setCounter] = useState(0)
  const [doubleCounter, setDoubleCounter] = useState(0)

  useEffect(() => {
    setDoubleCounter(counter * 2)
  }, [counter])

  return <p>{doubleCounter}</p>
}

Why is this wrong? Because we are forcing React to update twice without any reason. The first time it updates when the counter changes, and the second time when the doubleCounter changes.

Another very common use case is computing a state from the prop:

// with useEffect
import React, { useEffect } from 'react'

const Example: React.FC<{ property: string[] }> = ({ property }) => {
  const [counter, setCounter] = useState(0)
  const [transformedProperty, setTransformedProperty] = useState<string[]>([])

  useEffect(() => {
    const filtered = property.filter(p => p === 'derived')

    setTransformedProperty(filtered)
  }, [property])

  return <p>{doubleCounter}</p>
}

// without useEffect
import React, { useEffect } from 'react'

const Example: React.FC<{ property: string[] }> = ({ property }) => {
  const [counter, setCounter] = useState(0

  const transformedProperty = property.filter(p => p === 'derived') // can be wrapped in useMemo for performance reason

  return <p>{doubleCounter}</p>
}

Now, how many times have you seen data fetched from the API, and then in useEffect it gets transformed and stored in a state? Transformed data shouldn’t be stored in a state variable, it should be stored in a regular JS variable to avoid unnecessary reconciliations like in the first code example.

# react