React中如何避免EditableRow的Input触发onChange时UsersList重渲染
解决React中EditableRow输入触发时UsersList不必要重渲染的问题
核心原理
React 父组件状态更新时,默认会重渲染所有后代组件,要阻断这个链路,需要同时做子组件缓存和回调引用稳定两个处理。
具体实现步骤
- 第一步:用
React.memo包裹EditableRow组件
给子组件加一层浅比较逻辑,只有组件props发生变化时才会触发重渲染,默认的浅比较就能满足大多数场景:// EditableRow.tsx import React, { memo } from 'react'; interface EditableRowProps { user: User; onChange: (userId: number, field: keyof User, value: string) => void; } const EditableRow = memo(({ user, onChange }: EditableRowProps) => { // 原有组件逻辑,比如input的onChange绑定 return ( <div> <input value={user.name} onChange={(e) => onChange(user.id, 'name', e.target.value)} /> <input value={user.email} onChange={(e) => onChange(user.id, 'email', e.target.value)} /> </div> ) }); export default EditableRow; - 第二步:用
useCallback缓存UsersList中传给子组件的onChange回调
如果直接在UsersList的render中定义箭头函数传给EditableRow,每次UsersList重渲染都会生成新的函数引用,导致React.memo的浅比较判断props变化,依然触发重渲染,所以要用useCallback缓存函数引用:// UsersList.tsx import React, { useState, useCallback } from 'react'; import EditableRow from './EditableRow'; interface User { id: number; name: string; email: string; } const UsersList = () => { const [users, setUsers] = useState<User[]>([]); // 用useCallback缓存回调,依赖项为空保证引用永远不变 const handleUserChange = useCallback((userId: number, field: keyof User, value: string) => { // 状态更新用函数式写法,不需要依赖外部的users变量,避免依赖项变化导致回调重新生成 setUsers(prevUsers => { return prevUsers.map(user => { if (user.id === userId) { return { ...user, [field]: value }; } return user; }) }) }, []); // 依赖项为空,回调引用永远不变 return ( <div> {users.map(user => ( <EditableRow key={user.id} // 用稳定的id作为key,不要用数组索引 user={user} onChange={handleUserChange} /> ))} </div> ) }; export default UsersList; - 第三步:保证传递给
EditableRow的user对象引用稳定
不要在渲染EditableRow的时候临时构造新的user对象传递,直接使用useState中存储的原始user对象即可,避免引用变化触发memo的重渲染判断。
验证方式
可以在UsersList组件内加console.log,修改EditableRow的输入时,UsersList的log只会在第一次初始化和users列表整体替换的时候打印,不会每次输入都触发。
内容的提问来源于stack exchange,提问作者Harman
相关产品推荐
相关产品推荐

