假设我有一个react状态变量,它是一个用户对象数组。用户对象有两个对我们很重要的键: id和name。我必须更改该列表中两个用户的名称。此代码如下
`
const App = () => {
const [users, setUsers] = useState([]);
useEffect(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const users = await response.json();
setUsers(users);
}, [])
const edit1 = (id) => {
const newUsers = users.map(user => {
if(user.id === id) {
user.name = 'John Doe';
}
return user;
});
setUsers(newUsers);
}
const edit2 = (id) => {
const newUsers = users.map(user => ({
...user,
name: user.id === id ? 'Jane Doe' : user.name
}));
setUsers(newUsers);
}
return(
<div>
<button onClick={() => edit1(2)}>Edit-1</button>
<button onClick={() => edit2(5)}>Edit-2</button>
<ul>
{users.map((user) => (<li key={user.key}>{user.name}</li>))}
</ul>
</div>
);
}`
哪种方法更好?在edit1和edit2之间,哪种方法更好,因为它们都完成了任务?或者还有其他方法可以做到这一点?
问题是相当直接的。在edit1中,没有创建新对象,而是创建了一个新数组。因为rerender只检查对数组对象的引用是否已经更改,因此它会转到rerender。在edit2中,还创建了新的对象以及新的数组。这也会导致改进机。我搞不懂哪种方法更好。
发布于 2022-11-21 19:34:08
使用edit2,您可以在内部创建一个新数组和新元素。但是使用edit1,即使您有一个新的数组,内部的对象仍然对旧的对象有参考价值。所以我建议edit2。
发布于 2022-11-21 19:52:26
您应该使用"setState“的函数版本。这样就可以获取当前状态,这样您就可以不依赖于状态本身来设置它。如果你在useEffect里面有这个,你可以无限循环。
setUsers((existingUsers) =>
existingUsers.map((user) => ({
...user,
name: user.id === id ? "John Doe" : user.name // Apply change within spread
}))
);如果您想避免扩散;因为这可能会对深嵌套结构产生问题,您可以使用一个不可变的库,比如Immer。此外,Immer是由Redux使用。
setUsers(
produce((draftUsers) => {
draftUsers.forEach((user) => {
if (user.id === id) {
user.name = "Jane Doe"; // Directly mutate the draft state
}
});
})
);Immer还有一个useImmer钩子,您可以使用它来改变状态。这样就不需要将produce调用放在状态更新的中间。
有关更多信息,请参见:https://github.com/immerjs/use-immer
const [users, updateUsers] = useImmer([]); // use-immer hook
const edit3 = (id) => {
updateUsers((draftUsers) => {
draftUsers.forEach((user) => {
if (user.id === id) {
user.name = "Jane Doe";
}
});
});
};工作实例
有关活动版本,请参见CodeSandbox。
import { useEffect, useState } from "react";
import produce from "immer";
const App = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
(async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
const users = await response.json();
setUsers(users);
})();
}, []);
// Spread existing
const edit1 = (id) => {
setUsers((existingUsers) =>
existingUsers.map((user) => ({
...user,
name: user.id === id ? "John Doe" : user.name
}))
);
};
// Using Immer
const edit2 = (id) => {
setUsers(
produce((draftUsers) => {
draftUsers.forEach((user) => {
if (user.id === id) {
user.name = "Jane Doe";
}
});
})
);
};
return (
<div>
<button onClick={() => edit1(2)}>Edit-1</button>
<button onClick={() => edit2(5)}>Edit-2</button>
<ul>
{users.map((user) => (
<li key={user.key}>{user.name}</li>
))}
</ul>
</div>
);
};
export default App;https://stackoverflow.com/questions/74523910
复制相似问题