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;
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.