DEV Community

Cover image for We need to talk about state in React

We need to talk about state in React

andyrichardsonn profile image Andy Richardson Updated on ・3 min read

Everyone loves an opportunity to slam on the big dog of frontend - React; but when it comes to state, it really is first class!

Let's talk about state and explore how to make managing it a breeze.

Literal forms of state

First off, it helps to understand the two forms that state can take in an application.

Explicit state

In the case of modern React, this is useState and useReducer. Explicit state doesn't just come out of thin air - it has to be explicitly created and managed.

Derived state

A psuedostate of sorts, derived state is a result of processing one or more states (explicit or derived).

const [input, setInput] = useState(); // Explicit state
const inputValid = useMemo(           // Derived state
  () => input && input.length > 6,

Choosing types of state

Knowing whether to use explicit or derived state might seem challenging - but there's a really simple answer.

Always use derived state where possible

Forgetting to stick with the above rule can lead to redundant state.

Unlike redundant code, redundant state is a real problem that actually exists; and can have an impact on everything from performance to maintainability.

Spotting redundancy

If you've ever written something like the following - I know I have - you've probably been guilty of creating redundant state.

const [value, setValue] = useState("");
const [isValid, setIsValid] = useState(false);

  () => setIsValid(value && value.length > 4), 

A useEffect call which immediately calls a setState function is almost always an example of state that should be derived.

It doesn't seem like the worst thing in the world, and on it's own, it probably isn't. That being said, if this pattern exists, there's a good chance it exists in many places and can lead to a larger problem.

useEffect hell

Most of us have been on a project that has gone through useEffect hell. Trying to fix that one bug but being unable to trace it because a single state change causes a flurry of new renders.

The thing with useEffect is it can cause a cascading number of state updates... which in turn, can cause subsequent useEffect calls. This isn't an issue with the function itself - it's an issue with excessive state.

Tips for managing state

If I had one piece of advice for managing state, it would be to keep it to a minimum... but I don't have one just one piece of advice - so here's some more!

Batch state updates

When multiple state updates are being called at one time, it's useful to batch these together into one call.

With batching
const [{ fetching, data }, setState] = useState({ 
  fetching: true, 
  data: undefined 

useEffect(() => {
  getData.then(data => setState({
    fetching: false,
}, []);

// State 1: { fetching: true, data: undefined }
// State 2: { fetching: false, data: 1234 }
Without batching
const [fetching, setFetching] = useState(true);
const [data, setData] = useState();

useEffect(() => {

  (async () => {
    const data = await getData();
}, []);

// State 1: { fetching: true, data: undefined }
// State 2: { fetching: false, data: undefined } 
// State 3: { fetching: false, data: 1234 }

Batched updates don't just mean fewer renders, there will be fewer possible states to deal with; making testing and reproductions much simpler.

Remember, fewer renders means fewer state changes

Use fixtures

Fixtures (or stories) are an incredible tool for understanding, modelling, and documenting all the states of your app.

Find out more about fixtures over here.

Try useMemo more often

It's surprising how much of an impact it can make.

Hopefully, you found this interesting! If you have any thoughts or comments, feel free to drop them below or hit me up on twitter - @andyrichardsonn

Disclaimer: All thoughts and opinions expressed in this article are my own.

Discussion (0)

Forem Open with the Forem app