Derived state in React

Derived state in React

Don't manage state, derive it

What is a derived state?

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.