Avoid deriving state from props in React
A common footgun in React projects is derived state, which means basing state on props. This can be directly copying props into state, or deriving state from props with some extra logic.
This often causes trouble because once the state is set based on props, it won’t be updated by subsequent updates to props. This is after all the purpose of props and state in React: props are clean and stateless, and state is of course stateful.
To give a minimal example, here’s a parent component with a child component in JSX:
<Parent>
<Child foobar={100}>
</Parent>
The parent is passing a value of 100
for the child’s foobar
prop.
The child component implementation might look like this:
class Child extends React.Component {
state = {
derivedThing: this.props.foobar * 2
}
}
The derivedThing
state on Child
is derived state as it’s derived from the
foobar
prop that is passed in.
This is likely to cause problems if the parent component does an initial render with a default value and then a subsequent render with a new value, for example after fetching some data from the server.
For example, if the parent is a functional component it might look like this:
(This has a bug due to derived state!)
const Parent = () => {
const [foobar, setFoobar] = useState(0)
useEffect(() => {
fetch("example.com/foobar.json")
.then(res => res.json())
.then(json => setFoobar(json.foobar))
, [])
return (
<div>
<Child foobar={foobar}>
</div>
)
}
This will lead to a bug:
- The Parent initialises
foobar
to0
- The Parent renders Child with the
foobar
prop as0
- The Child initialises its
derivedThing
state to0
- The Parent’s
useEffect
call fetches the real value forfoobar
, e.g.100
- The Parent re-renders the Child with the
foobar
prop as100
- The Child has already initialised its derived state, so it ignores the new prop value.
- The Child is now stuck with its
derivedThing
state as0
.
This is quite a common footgun in React, which is why it’s best to avoid derived state whenever possible.
It might help to remember the rule of thumb that state is for interactivity. In other words, state is not for general variables in the program, it’s for interactivity with a human user, with state that will change over the course of the React process lifetime due to user actions.