Using too many useState hooks in react. How do I refactor this?
Asked Answered
S

2

11

the useState hooks are great. I mainly use the useState hooks to initialise certain states, and I also pass the function to children components to change the states. However, I realise I am starting to use too many useState hooks in my parent page component. This looks and feels wrong, because I am starting to have about 6-10 useState hooks in the parent page component.
Without showing the code, Is there a better way to do this? Maybe a better practice, or a better way to refactor.
Thanks

Superable answered 1/7, 2020 at 16:46 Comment(3)
If your state gets frequently changed in predictable ways and the state is related (e.g., not just a bunch of inputs in a form with different values), then useReducer might be a good way to go.Denis
This might help. reactjs.org/docs/…Cumshaw
I agree with Robert Moore. I recently refactored a component that was bloated with individual states and it is a major improvement. Just to add to the suggested reading kentcdodds.com/blog/should-i-usestate-or-usereducerPaschal
E
24

Whenever you encounter a problem like this you should first look if you can split your component into multiple smaller ones. However there are scenarios where that's not an option. In those cases I would advice using useReducer.

// before

const {cache, setCache } = useState({});
const {posts, setPosts } = useState({});
const {loading, setLoading } = useState(false);

// Would become after refactor

const initialState = {
  cache: {},
  posts:{},
  loading: false
}

const [state, dispatch] = useReducer(reducer, initialState);
Eritrea answered 1/2, 2021 at 9:27 Comment(3)
This looks so much simpler and more manageable. Thanks!Superable
its even more performant from my experienceEnjambment
It only solves management tidyup. Performance is better handled by decoupling as much of the states into separate related states. Ie use multiple useReducer or useContext that diffs specifics rather than one large tree that diffs everything on any change.Kun
K
0

I believe the best way of optimising useStates can be done by decoupling the unnecessary relationships. Whether you do that Inside the react component with useState, share them with useContext, useReducer or any other method. Putting everything in a single object tree does not optimise performance. So changing the above into a single useReducer merely moves the problem elsewhere.

The most optimised solution is going to be to only couple the necessary dependencies. Ie use multiple state handlers - ie multiple context providers, or multiple useReducers, zustand or do something clever with new useSignal which is essentially going to do the same thing by only listening on the minimum amount of diffs.

You can still subscribe between those multiple states if needed via a central useEffect.

A crude example with useContext:


<ProviderStateX>
  <ProviderStateY>
    <ProviderStateZ>

      <MyComNeedsX>
        <MyCompNeedsXY>
          <MyCompChildNeedsY>
        </>
        <MyCompNeedsXZ>
          <MyCompChildNeedsZ>
        </>
      </> 
    </> 
  </> 
</> 

There are tidier options without nesting Providers, but that example is for 'context' pardon the pun.

A better, cleaner solution is probably going to be using signal, multiple zustand states or native js proxies and only listening in the single thing that your react component cares about to prevent unnecessary re-render.

Kun answered 27/6, 2023 at 7:13 Comment(2)
For me that would pretty much the moment where I'd start to think about using a library like zustand or even redux for state management.Eritrea
@Eritrea Whether you use redux or anything else the same problem applies. The rule is don't observe everything on the same listener. Observe on different listeners - hence the above example using multiple contexts to emulate separate listeners. The problem with redux is that it 'listen's at the root, not matter what you change down the line.Kun

© 2022 - 2024 — McMap. All rights reserved.