React UseState Hell:-
This is a component to update a calendar event. Sadly, it has a number of problems.
import { useState } from "react";
There’s nothing preventing you from choosing an end date that’s before the start date, which makes no sense. There’s also no guard for a title or description that is too long. we’re calling set*() function
we will remember to validate all state variables before updating to the state, but I don’t rest easy knowing things are just sitting in such an easy-to-break state.
With the help of UseReducer, we could transform the above code, to just this:-
import { useReducer } from "react";
The useReducer
hook helps you control transformations from state A to state B.But many developers don't use useReduces they are using useState by making a common useState in the form of object like this:-
import { useState } from "react";
There’s one very important thing to consider here. Besides that this format still hopes that you always remember to spread it on ...event
so you don’t mess up by mutating the object directly (and subsequently causing React to not rerender as expected), it still misses out on the most critical benefit of useReducer
— the ability to supply a function that controls state transitions.
Going back to using useReducer
, the only difference is you get an additional argument that is a function that can help us ensure that each state transition is safe and valid:-
const [event, updateEvent] = useReducer(
(prev, next) => {
// Validate and transform event to ensure state is always valid
// in a centralized way
// ...
},
{ title: "", description: "", attendees: [] }
);
This has the major benefit of guaranteeing that your state is always valid, in a fully centralized way.So with this model, even if future code is added over time, and future developers on your team call updateEvent()
with potentially invalid data, your callback will always fire.For instance, we may want to always ensure, no matter how and where state is written, that the end date is never before the start date (as that would make no sense), and that the title has a max length of 100 characters:
import { useReducer } from "react";
useReducer
virtually anywhere you’d use useState:-
Now I am taking a basic example of Counter and using useState hook-
import { useState } from "react";
But even in this small example, should count
be able to go infinitely high? Should it ever be negative?
Ok, maybe it’s not so conceivable how a negative value could be achieved here, but if we wanted to set a count limit, it’s trivial useReducer
and we can have complete confidence that the state is always valid, regardless of where and how it’s written to.
import { useReducer } from "react";
useState
vs useReducer
?1. Next state depends on the previous:-
It is always better to use this method when the state depends on the previous one. It will give you a more predictable state transition.
2. Complex state shape:-
When the state consists of more than primitive values, like nested objects or arrays.
3. Easy to test:-
Reducers are pure functions, and this means they have no side effects and must return the same outcome given the same arguments. It is easier to test them because they do not depend on React.