State, Reducers, And useReducer In React

State, Reducers, And useReducer In React

To date, I’ve struggled to understand reducers. Terms like reducers, actions, dispatch, all blurred together and even though I could use, and in some cases, extend the redux store on some projects, I never understood all of the pieces.

Despite reading the hooks documentation on useReducer, the pieces didn’t click until I read Robin Wieruch’s two-part tutorial on reducers.¹ ² ³

So, what’s exactly going on?

useReducer

The useReducer returns a tuple [state, dispatch] and takes three arguments, reducerinitialArg, and init. Note: init is optional and used for lazy initialization - more on that in a minute.

In a Javascript file then, you would see something like:

import React, { useReducer } from 'react';
...

function FunctionalComponent() => { const [state, dispatch] = useReducer(reducer, initialArg, init); return ( <> {/* ... */} </> ) };

Notice, that at this point, this looks very similar to useState:

import React, { useState } from 'react';
...

function FunctionalComponent() => { const [value, setValue] = useState(initialValue); return ( <> {/* ... */} </> ) };

In fact, even if initialValue is something more exotic than a boolean or string, we can still use useState. We would just need to use the functional update syntax.

For example, adapting the React team’s example:

const initialValues = {
  buttonOne: 0,
  buttonTwo: 0,
}

function Counter() { const [count, setCount] = useState(initialValues); return ( <> Count: {count} <button onClick={() => setCount(initialValues)}>Reset</button> <button onClick={() => setCount(prevCount => {...prevCount, prevCount.buttonOne + 1)}>+</button> <button onClick={() => setCount(prevCount => {...prevCount, prevCount.buttonTwo - 1)}>-</button> </> ); }

This example isn’t very useful as the two values can only go in opposite directions, but it illustrates how we can use useState to manage more complicated state objects.

Why useReducer?

Since we can manage state with useState, why do we need useReducer at all? Per the React team:

useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks .— React Team

Using Reducers

useReducer accepts a reducer, a function in the form of (state, action) => newState.

Let’s simplify our example for the moment and just add numbers, but use useReducer:

const initialValues = 0;

function reducer = (state, action) => { return state + 1 }

function Counter() { const [state, dispatch] = useState(reducer, initialValues); return ( <> Count: {state.count} <button onClick={() => dispatch()}>+</button> </> ); }

The reason we only add numbers here, is because our reducer doesn’t usethe second argument, action. It’s fixed.

How might we change that?

Actions

Actions are how we change that.

From Redux documentation:

Actions are payloads of information that send data from your application to your store. They are the only source of information for the store.⁴

Here’s an example using the simplest of actions — again reintroducing our second button:

const initialValues = 0;

function reducer = (state, action) => { switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; }

function Counter() { const [state, dispatch] = useState(reducer, initialValues); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'INCREMENT'})}>+</button> <button onClick={() => dispatch({type: 'DECREMENT'})}>-</button> </> ); }

When we hit the + we dispatch the action to increment while the -dispatches an action. Those actions are evaluated by our reducer and return a new state.

Payload

The convention for writing an Action is to have both a type and a payloadkey. While the type is the what, the payload is the how. It doesn’t make much sense in this case since the state we’ve been using is just an integer, but what would happen if it were something more complicated? How might we change it then?

Let’s imagine a state object that has both our count and a person attribute.

const initialValues = {
  count: 0,
  person: {
    firstName: 'John',
    lasttName: 'Doe',
    age: '30',
  },
};

function reducer = (state, action) => { switch (action.type) { case 'INCREMENT': return {...state, count: state.count + action.payload}; case 'DECREMENT': return {...state, count: state.count - action.payload} default: throw new Error(Unknown action type, ${action.type}); }

function Counter() { const [state, dispatch] = useState(reducer, initialValues); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'INCREASE', payload: 1})}>+</button> <button onClick={() => dispatch({type: 'DECREASE', payload: 1})}>-</button> </> ); }

NB: In this case, we spread the state object before modifying the countattribute so that we don’t overwrite the whole object and avoid having our new value for the count be overwritten (order matters).

Lazy Initialization

Now that we know how to use actions, we can pull it all together to see how we would use a lazy initialization.

For example:

function init(initialValues){
return (
  { count: 0,
    person: {
      firstName: 'John',
      lasttName: 'Doe',
      age: '30'
    },
  }
)};

function reducer = (state, action) => { switch (action.type) { case 'INCREMENT': return {...state, count: state.count + action.payload}; case 'DECREMENT': return {...state, count: state.count - action.payload} case 'RESET': return init(action.payload); default: throw new Error(Unknown action type, ${action.type}); }

function Counter() { const [state, dispatch] = useState(reducer, initialValues, init); return ( <> <button onClick={() => dispatch({type: 'RESET', payload: initialValues})>Reset</button> Count: {state.count} <button onClick={() => dispatch({type: 'INCREASE', payload: 1})}>+</button> <button onClick={() => dispatch({type: 'DECREASE', payload: 1})}>-</button> </> ); }

This is often used in an example like the above where we want to extract the ability to reset the value outside of setting it initially. We do this in the above with the Reset button element.

Conclusion

When I came across a project that used Redux or another state management tool, I never really understood how it all worked. I could use it, butt I never felt comfortable.

After reading through Robin’s tutorials, I was able to return with fresh eyes and implemented it within my own project. It’s a great feeling when things click! Hopefully this write up will help someone else experience that same feeling.

Further reading

☞ React - The Complete Guide (incl Hooks, React Router, Redux)

How to Build a Decentralized Todo List with React and Blockstack

☞ Modern React with Redux [2019 Update]

☞ The Complete React Developer Course (w/ Hooks and Redux)

☞ React JS Web Development - The Essentials Bootcamp

☞ React JS, Angular & Vue JS - Quickstart & Comparison

☞ The Complete React Js & Redux Course - Build Modern Web Apps

☞ React JS and Redux Bootcamp - Master React Web Development


Originally published on https://www.stephencharlesweiss.com

reactjs react-native redux user-experience

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How native is React Native? | React Native vs Native App Development

Article covers: How native is react native?, React Native vs (Ionic, Cordova), Similarities and difference between React Native and Native App Development.

Hire Dedicated React Native Developer

Have you ever thought of having your own app that runs smoothly over multiple platforms? React Native is an open-source cross-platform mobile application framework which is a great option to create mobile apps for both Android and iOS. **[Hire...

Accessing Redux from Components In React & React Native

How to set up a basic version of Redux in your React or React Native application. To make things clearer, I based my setup on my event application, where users create events that other users attend. We generated the action creators, reducers, and Redux store, and wrapped the application in a provider.

A Basic Redux Setup in React/React Native

Redux is a powerful state management tool that can be very useful as your React or React Native application grows and requires you to keep track of more state.

Build a simple React Native Pokemon app with React-Navigation

As we start learning new technologies we want to start building something or work on a simple project to get a better understanding of the technology.