Home > Software engineering >  react hooks: What is the disadvantage of directly changing the nested object?
react hooks: What is the disadvantage of directly changing the nested object?

Time:11-04

import { useState } from "react";

function App() {
  const [form, setForm] = useState({
    usename: ``,
    password: ``,
    avatar: { id: 0, url: ``, deep: { id: 0 } },
  });
  return (
    <div>
      <h1>ID:{form.avatar.deep.id}</h1>
      <h2
        onClick={() => {
          form.avatar.deep.id  = 1;
          setForm({ ...form });
        }}
      >
        Change-ID
      </h2>
    </div>
  );
}

export default App;

codesandbox-online-run

I learn reat hooks, people tell me do not change nested object.

We should keep the original state unchanged or use https://github.com/immerjs/immer

But the above code can also update the UI, why ?

What are form.avatar.deep.id =1;setForm({ ...form }); disadvantages ?

What are the disadvantages of changing the attributes of the nested object directly?

CodePudding user response:

What are form.avatar.deep.id =1;setForm({ ...form }); disadvantages ?

React is designed with immutable state in mind. This allows doing a very cheap === comparison to tell if things have changed. You've copied form, and that is enough to get this sample code to work. The old and new form are different objects, so the component rerenders when you set state.

The problem comes if some piece of code cares about a portion of the form, not the entire form. If a component wants to check whether form.avatar has changed, it will see that no, it has not changed, but in fact it has!

For example, suppose we split out an Avatar component as follows:

import { memo } from 'react';

function App() {
  const [form, setForm] = useState({
    usename: ``,
    password: ``,
    avatar: { id: 0, url: ``, deep: { id: 0 } },
  });

  return (
    <div>
      <Avatar avatar={form.avatar} />
      <h2
        onClick={() => {
          form.avatar.deep.id  = 1;
          setForm({ ...form });
        }}
      >
        Change-ID
      </h2>
    </div>
  );
}

const Avatar = memo(function ({ avatar }) {
  useEffect(() => {
    // Supposed to do some stuff, if avatar has changed
  }, [avatar]);

  return (
    <h1>ID:{avatar.deep.id}</h1>
  )
})

In the above code, both memo and useEffect are broken. memo is supposed to render the component if the props changed, and skip rendering if it didn't change. But it will look like avatar never changes, and so it will never rerender . Similarly, if the component does render, the use effect will forever think that avatar is not changing, and will not rerun the effect.

  • Related