Dont use UseState Hook anymore in your next Project

Posted By :Aadarsh Pathak |20th February 2023

React  UseState Hell:-

This is a component to update a calendar event. Sadly, it has a number of problems.


import { useState } from 'react';

function EditCalendarEvent() {
  const [startDate, setStartDate] = useState();
  const [endDate, setEndDate] = useState();
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');
  const [location, setLocation] = useState();
  const [attendees, setAttendees] = useState([]);
  return (
    <>
      <input value={title} onChange={(e) => setTitle(e.target.value)} />
      {/* ... */}
    </>
  );
}

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.

There is a more powerful alternative to useState Hook:-

With the help of UseReducer, we could transform the above code, to just this:-

import { useReducer } from 'react';
function EditCalendarEvent() {
  const [event, updateEvent] = useReducer(
    (prev, next) => {
      return { ...prev, ...next };
    },
    { title: '', description: '', attendees: [] }
  );
  return (
    <>
      <input
        value={event.title}
        onChange={(e) => updateEvent({ title: e.target.value })}
      />
      {/* ... */}
    </>
  );
}

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';
function EditCalendarEvent() {
  const [event, setEvent] = useState({
    title: '',
    description: '',
    attendees: [],
  });
  return (
    <>
      <input
        value={event.title}
        onChange={(e) => setEvent({ ...event, title: e.target.value })}
      />
      {/* ... */}
    </>
  );
}

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';
function EditCalendarEvent() {
  const [event, updateEvent] = useReducer(
    (prev, next) => {
      const newEvent = { ...prev, ...next };
      // Ensure that the start date is never after the end date
      if (newEvent.startDate > newEvent.endDate) {
        newEvent.endDate = newEvent.startDate;
      }
      // Ensure that the title is never more than 100 chars
      if (newEvent.title.length > 100) {
        newEvent.title = newEvent.title.substring(0, 100);
      }
      return newEvent;
    },
    { title: '', description: '', attendees: [] }
  );
  return (
    <>
      <input
        value={event.title}
        onChange={(e) => updateEvent({ title: e.target.value })}
      />
      {/* ... */}
    </>
  );
}
This ability to prevent direct mutation of state gives us a major safety net, especially as our code grows in complexity over time.

You can use useReducer virtually anywhere you’d use useState:-

Now I am taking a basic example of Counter and using useState hook-

import { useState } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>Count is {count}</button>;
}

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';
function Counter() {
  const [count, setCount] = useReducer((prev, next) => Math.min(next, 10), 0);
  return <button onClick={() => setCount(count + 1)}>Count is {count}</button>;
}

So when should I use 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.

 

 

About Author

Aadarsh Pathak

Aadarsh is an experienced Frontend Developer with expertise in ReactJS, HTML, CSS, JavaScript, and jQuery. With over a year of industry experience, he has contributed to various client and internal projects. One of his notable contributions includes the Reception Module Portal Development (KRB), which is a ReactJS project with a Microservices architecture, encompassing modules such as Reception, Billing, and Physician. Aadarsh's creative mindset and analytical skills enable him to think critically and adapt to new technologies. He is dedicated to staying up-to-date with industry trends through continuous learning and reading.

Request For Proposal

[contact-form-7 404 "Not Found"]

Ready to innovate ? Let's get in touch

Chat With Us