useState vs useReducer: What are they and when to use them?

It is common to see useState hook used for state management, However React also have another hook to manage component’s state, Which is useReducer hook. In fact, useState is built on useReducer!. So a question arises: What’s the difference between the …


This content originally appeared on DEV Community and was authored by m0nm

It is common to see useState hook used for state management, However React also have another hook to manage component's state, Which is useReducer hook. In fact, useState is built on useReducer!. So a question arises: What's the difference between the two ? And when should you use either ?

useState hook:

useState hook is a hook used to manipulate and update a functional component. The hook takes one argument which is the initial value of a state and returns a state variable and a function to update it.

const [state, setState] = useState(initialValue)

So a counter app using the useState hook will look like this:

function Counter() {
  const initialCount = 0
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(count - 1)}>Decrement</button>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </>
  );
}

useReducer hook:

this hook is similar to the useState hook. However it's able to handle more complex logic regarding the state updates. It takes two arguments: a reducer function and an initial state. The hook then returns the current state of the component and a dispatch function

const [state, dispatch] = useReducer(reducer, initialState)

the dispatch function is a function that pass an action to the reducer function.

The reducer function generally looks like this:

const reducer = (state, action) => {
    switch(action.type) {
        case "CASE1": 
            return "new state";
        case "CASE2": 
            return "new state";
        default:
            return state
    }
}

The action is usually an object that looks like this:

// action object:
{type: "CASE1", payload: data}

The type property tells the reducer what type of action has happened ( for example: user click on 'Increment' button). The reducer function then will determine how to update the state based on the action.

So a counter app using the useReducer hook will look like this:

const initialCount = 0

const reducer = (state, action) => {
    switch (action.type) {
        case "increment":
            return action.payload;

        case "decrement": 
            return action.payload;

        case "reset": 
            return action.payload;

        default: 
            return state;
    }
}

function Counter() {
    const [count, dispatch] = useReducer(reducer, initialCount)

    return (
    <>
      Count: {count}
      <button onClick={() => dispatch({type: "reset", payload: initialCount}))}>Reset</button>
      <button onClick={() => dispatch({type: "decrement", payload: state - 1})}>Decrement</button>
      <button onClick={() => dispatch({type: "increment", payload: state + 1})}>Increment</button>
    </>
  );
}

When should i useReducer() ?

As stated above, The useReducer hook handles more complex logic regarding the state updates. So if you're state is a single boolean, number, or string, Then it's obvious to use useState hook. However if your state is an object (example: person's information) or an array (example: array of products ) useReducer will be more appropriate to use.

Let's take an example of fetching data:

If we have a state that represent the data we fetched from an API, The state will either be one of this three 'states': loading, data, or error

When we fetch from an API, Our state will go from loading ( waiting to receive data), to either data or we'll get an error

Let's compare how we handle state with the useState hook and with the useReducer hook

  • With the useState hook:
function Fetcher() {
    const [loading, setLoading] = useState(true)
    const [data, setData] = useState(null)
    const [error, setError] = useState(false)

    useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/posts/1').then(res => {
            setLoading(false)
            setData(res.data)
            setError(false)
        }).catch((err) => {
            setLoading(false)
            setData(null)
            setError(true)
        })
        ,[])

        return (
        {loading ? <p>Loading...</p> 
         : <div>
            <h1>{data.title}</h1>
            <p>{data.body}</p>
         </div> }
        {error && <p>"An error occured"</p> }
        )

}
  • With the useReducer hook:

const initialState = {
    loading: true,
    data: null,
    error: false
}

const reducer = (state, action) => {
    switch (action.type) {
        case "SUCCESS":
            return {
                loading: false,
                data: action.payload,
                error: false
            };

        case "ERROR": 
            return {
                loading: false,
                data: null,
                error: true
            };

        default:
            return state;
    }
}

function Fetcher() {
    const [state, dispatch] = useReducer(reducer, initialState)

    useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts/1').then(res => {
        dispatch({type: "SUCCESS", payload: res.data})
    }).catch(err => {
        dispatch({type: "ERROR"})
    })

    } ,[])

    return (
        {state.loading ? <p>Loading...</p> 
         : <div>
            <h1>{state.data.title}</h1>
            <p>{state.data.body}</p>
         </div> }
        {state.error && <p>"An error occured"</p> }
        )

}

As you can see with the useReducer hook we've grouped the three states together and we also updated them together. useReducer hook is extremely useful when you have states that are related to each other, Trying to handle them all with the useState hook may introduce difficulties depending on the complexity and the bussiness logic of it.

Conclusion

To put it simply: if you have a single state either of a boolean, number, or string use the useState hook. And if you're state is an object or an array, Use the useReducer hook. Especially if it contains states related to each other.

I hope this post was helpful, Happy coding!


This content originally appeared on DEV Community and was authored by m0nm


Print Share Comment Cite Upload Translate Updates
APA

m0nm | Sciencx (2022-02-19T13:17:57+00:00) useState vs useReducer: What are they and when to use them?. Retrieved from https://www.scien.cx/2022/02/19/usestate-vs-usereducer-what-are-they-and-when-to-use-them/

MLA
" » useState vs useReducer: What are they and when to use them?." m0nm | Sciencx - Saturday February 19, 2022, https://www.scien.cx/2022/02/19/usestate-vs-usereducer-what-are-they-and-when-to-use-them/
HARVARD
m0nm | Sciencx Saturday February 19, 2022 » useState vs useReducer: What are they and when to use them?., viewed ,<https://www.scien.cx/2022/02/19/usestate-vs-usereducer-what-are-they-and-when-to-use-them/>
VANCOUVER
m0nm | Sciencx - » useState vs useReducer: What are they and when to use them?. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/02/19/usestate-vs-usereducer-what-are-they-and-when-to-use-them/
CHICAGO
" » useState vs useReducer: What are they and when to use them?." m0nm | Sciencx - Accessed . https://www.scien.cx/2022/02/19/usestate-vs-usereducer-what-are-they-and-when-to-use-them/
IEEE
" » useState vs useReducer: What are they and when to use them?." m0nm | Sciencx [Online]. Available: https://www.scien.cx/2022/02/19/usestate-vs-usereducer-what-are-they-and-when-to-use-them/. [Accessed: ]
rf:citation
» useState vs useReducer: What are they and when to use them? | m0nm | Sciencx | https://www.scien.cx/2022/02/19/usestate-vs-usereducer-what-are-they-and-when-to-use-them/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.