DEV Community

Cover image for useImmer, an excelent alternative to useState
Jesus Jimenez Cordero
Jesus Jimenez Cordero

Posted on • Updated on

useImmer, an excelent alternative to useState

Surely you have ever found yourself in the situation where a useState looks longer than all the code together in your component, either because you have to update a very large object or because you need a large amount of code to access a single property within that object, which looks even worse if the value you are trying to access is very nested.

Whether you are new to React or you have been programming for years, this situation is not alien to you, since one of the problems (at least in my opinion) and advantages of React is the immutability of its values; which causes the situation I mentioned at the beginning. One of the solutions that I recently found to solve this situation, is the use of the Immer package and more specifically, the hooks that they created, called useImmer and useImmerReducer, both very useful.

An example of the problematic situation that I mention to you is the following one:

import React, { useState } from 'react';

const UserProfile = () => {
  const [user, setUser] = useState({
    id: 1,
    name: 'John Doe',
    email: 'johndoe@example.com',
    address: {
      street: '123 Main St',
      city: 'Anytown',
      state: 'CA',
      zip: '12345',
    },
    preferences: {
      theme: 'dark',
      notifications: true,
    },
  });

  const updateUser = (userId, newDetails) => {
    if (user.id === userId) {
      setUser((prevUser) => ({
        ...prevUser,
        ...newDetails,
      }));
    }
  };

  const handleChangeAddress = () => {
    const newAddress = {
      address: {
        street: '456 Another St',
        city: 'New City',
        state: 'NY',
        zip: '67890',
      },
    };
    updateUser(1, newAddress);
  };

  return (
    <div>
      <h1>User Profile</h1>
      <p>Name: {user.name}</p>
      <p>Email: {user.email}</p>
      <h2>Address</h2>
      <p>Street: {user.address.street}</p>
      <p>City: {user.address.city}</p>
      <p>State: {user.address.state}</p>
      <p>Zip: {user.address.zip}</p>
      <button onClick={handleChangeAddress}>Change Address</button>
    </div>
  );
};

export default UserProfile;
Enter fullscreen mode Exit fullscreen mode

Probably, for you, at this moment, this update does not look abnormal, because you are probably already used to see it in React, but the reality is that if more than one person had to read this code it would be a punishment. As programmers we should always consider that code readability is a priority so we don't depend on comments (except in specific situations) or KTs that can take time that can be used for development. This code can look much cleaner and it should be so, because if we analyze it with a little perspective, this code is not doing a very complex update, since it is only updating the address and nothing else, now imagine if we had to update more properties of this object, each one at a different nesting level, it would be very complex to write and read, wouldn't it?

This is an example of the same code you just saw above, but now using useImmer. You'll be surprised at how easy it is to implement, but especially to read:

import { useImmer } from 'use-immer';

const UserProfile = () => {
  const [user, setUser] = useImmer({
    id: 1,
    name: 'John Doe',
    email: 'johndoe@example.com',
    address: {
      street: '123 Main St',
      city: 'Anytown',
      state: 'CA',
      zip: '12345',
    },
    preferences: {
      theme: 'dark',
      notifications: true,
    },
  });

  const handleChangeAddress = () => {
    setUser(draft => {
      if (draft.id === 1) {
        draft.address.street = '456 Another St';
        draft.address.city = 'New City';
        draft.address.state = 'NY';
        draft.address.zip = '67890';
      }
    });
  };

  return (
    <div>
      <h1>User Profile</h1>
      <p>Name: {user.name}</p>
      <p>Email: {user.email}</p>
      <h2>Address</h2>
      <p>Street: {user.address.street}</p>
      <p>City: {user.address.city}</p>
      <p>State: {user.address.state}</p>
      <p>Zip: {user.address.zip}</p>
      <button onClick={handleChangeAddress}>Change Address</button>
    </div>
  );
};

export default UserProfile;
Enter fullscreen mode Exit fullscreen mode

I think after this, you are now able to see a difference in the code and the advantage of implementing this hook. Probably at this moment it will seem that it is not so drastic as to want to replace the use of useState by useImmer, but surely you could consider implementing it in future projects that have large amounts of updates in a state, that surely can save you a lot of headaches.

The useImmerReducer hook has a very similar functionality and is just as useful in examples very similar to the one I just showed you. I recommend you to always try alternatives that help your code to be more readable, after all, you will not always be working alone and readability in the code is always useful for both you and your partners, because going back to an old code, even written by you, is complicated if it is not clear enough.

Top comments (0)