Creative ways of using useReducer | Code Insight

Creative ways of using useReducer
If you are a React developer, you probably know the hook useReducer and how to use it. You call useReducer passing a reducer function, an initial value, an optional initializer function, and it will return an array with two values: the current state and the dispatch function. If you don't know it yet, I invite you to check the official React documentation.
However, some developers, especially those coming from Redux, may think that useReducer is only used to manage complex state. Although it is the main use case, useReducer can be used creatively in some different ways due to functional JavaScript's permissive nature. Let's explore how useReducer works:
useReducer returns an array with two values: the current state and the dispatch function. The initial state is either the initialValue or the return value of the initializer function called with initialValue as an argument if we provide the third argument. The dispatch function is used to update the state. When we call it, it will internally invoke the reducer function with the current state as the first argument and the argument passed to dispatch as the second argument (called action in the React docs), and its return value will be the next state.
Technically, the reducer function takes the state and action as arguments and returns the next state. However, the only requirement for the reducer function is to return the next state. We can use the arguments, but they are not really required.
The same goes for the dispatch function. It expects to be invoked with an action argument, but we are free to call it with any type of argument or no argument at all. The only thing to remember is that the argument that we pass to it will be passed as the second argument to the reducer function.
Let's try it!
Simple toggle
Using useState
With useState, the logic should be placed where the setState is called, in this case, on the onChange handler.
Using useReducer
The reducer function just needs to return the next state, which is the opposite of the current state, and the initialValue is the initial state (true or false).
Bingo 🎉, now we don't need to handle the logic on the onChange handler anymore.
Increment Button
Using useState
When using useState, the logic for incrementing the state should be placed where the setState is called, which in this case is the onClick handler.
Using useReducer
When using useReducer, the reducer function just needs to return the nextState that is the currentState plus 1, and the initialValue is the initialState (a number).
If we want to give the option to set the value to be incremented, we could easily do it using the action argument of the dispatch function. We can even create a derived function decrementBy by invoking the incrementBy with a negative value.
Enhanced State
Sometimes, we want to apply some restrictions or formatting to the values on the state. Using useState, we need to be sure to apply them before calling setState, which can lead to bugs and regressions. Using useReducer, we have the possibility to add these restrictions or formatting to the reducer function and be sure that they will be applied on each state update.
Let's see how to build a state for the expiry date of a payment card!
Using useState
The logic for the expiry date lives inside the changeHandler function.
Using useReducer
The logic is moved to the reducer function.
Safe State
When working with complex state objects in React, it's important to ensure that updates are performed in a safe and efficient way. If we only update part of the state object and pass it to setState, the other properties will be lost, leading to potential errors and inconsistencies.
Using useState
One common pattern for updating complex state objects with useState is to define an updateState function that merges the current state with the new updates. This function can then be passed to child components as a prop to allow them to safely update the state.
Using useReducer
When using useReducer, the update logic is contained within the reducer function, which takes the current state and the action as arguments and returns the updated state. This allows us to easily perform safe updates to complex state objects without losing any information.
Conclusion
In summary, useReducer is a powerful tool for managing complex state in React applications. It allows us to easily perform safe updates to state objects without losing any information, and can be used in a variety of creative ways. By using useReducer, we can gain greater control over our state management and write more efficient and maintainable code.