React中如何编辑对象数组?edit1与edit2方案孰优?
React状态更新方案对比:edit1 vs edit2
我有一个React状态变量,是包含user对象的数组,user对象有id和name字段。现在需要修改列表中用户的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是直接修改原user对象,仅生成新数组;edit2则创建新对象和新数组。想知道哪种方案更优,还有没有其他实现方式?
方案优劣分析
edit2是更优的方案,原因如下:
- React状态更新遵循不可变原则:edit1中直接修改原user对象的
name属性,会污染原始状态。虽然这次通过新数组的引用变化触发了重渲染,但如果后续有组件使用React.memo、useMemo或useCallback依赖该user对象的引用,会因为原对象被修改导致无法正确触发更新,或引发不必要的重渲染。 - edit2通过扩展运算符
...user创建新的user对象,保留原对象其他属性的同时仅修改name字段,完全符合不可变更新规则,状态变化可预测,也能配合React的性能优化API正常工作。
其他实现方式
- 函数式状态更新:当更新依赖前一个状态时,使用
setUsers的函数形式,避免因状态异步更新导致的错误:
const edit3 = (id) => { setUsers(prevUsers => prevUsers.map(user => ({ ...user, name: user.id === id ? 'New Name' : user.name }))); }
- 用immer简化不可变更新:immer允许以"可变"的写法编写代码,内部自动转换为不可变更新,代码更简洁:
import { produce } from 'immer'; const edit4 = (id) => { setUsers(produce(prevUsers => { const targetUser = prevUsers.find(user => user.id === id); if (targetUser) { targetUser.name = 'Immer Updated Name'; } })); }
内容的提问来源于stack exchange,提问作者Parvez
相关产品推荐
相关产品推荐

