React中使用useMemo仍存在键盘输入延迟问题求助
输入框输入延迟问题排查与解决
我编写的代码包含大量输入框,每个价格值对应一个输入框用于修改。每个定价含3个价格,每个区域含3个定价,输入框数量较多。为避免每次输入触发全局重渲染,我给更新输入框值的函数使用了useMemo,同时采用useReducer简化输入控制逻辑。但输入数字时,字符无法即时显示,存在短暂延迟,连续输入时问题更明显。
相关代码
更新函数
const handleUpdate = useMemo( () => (property, valu, obid) => { dispatch({ type: "UPDATE_DATA", property, payload: valu, id: obid }); }, [dispatch] );
Reducer逻辑
function reducer(state, action) { switch (action.type) { // 其他case省略 case "UPDATE_DATA": return { ...state, data: state.data.map((item) => { if (item.id === action.id) { return { ...item, [action.property]: action.payload }; } return item; }), }; } }
问题根源
- 不必要的
useMemo开销:useReducer返回的dispatch函数本身是稳定的(组件重渲染时引用不会改变),因此handleUpdate不需要用useMemo缓存,额外的缓存判断反而增加了不必要的计算。 - 全局重渲染成本过高:每次输入都会触发
data数组的全量map操作,生成新的数组和修改项的新对象引用。这会导致所有依赖data的子组件都重新渲染,当data是长数组时,重渲染的性能开销会非常大,直接表现为输入延迟。
解决方案
1. 移除不必要的useMemo
直接定义handleUpdate即可,无需缓存:
const handleUpdate = (property, valu, obid) => { dispatch({ type: "UPDATE_DATA", property, payload: valu, id: obid }); };
2. 拆分组件并使用React.memo
将每个定价项拆分为独立子组件,用React.memo包裹,确保只有当前项的props变化时才重渲染:
import React from 'react'; // 缓存子组件,仅当props变化时重渲染 const PriceItem = React.memo(({ item, onUpdate }) => { return ( <div className="price-item"> <input type="number" value={item.price1} onChange={(e) => onUpdate('price1', e.target.value, item.id)} /> <input type="number" value={item.price2} onChange={(e) => onUpdate('price2', e.target.value, item.id)} /> <input type="number" value={item.price3} onChange={(e) => onUpdate('price3', e.target.value, item.id)} /> </div> ); }); // 父组件中渲染子组件 {state.data.map(item => ( <PriceItem key={item.id} item={item} onUpdate={handleUpdate} /> ))}
3. 优化Reducer的不可变更新逻辑
使用immer库简化不可变更新,避免手动全量map数组,减少对象复制开销:
首先安装immer:
npm install immer
然后修改Reducer:
import { produce } from 'immer'; function reducer(state, action) { switch (action.type) { case "UPDATE_DATA": return produce(state, draft => { // 直接找到目标项修改,无需全量遍历复制 const target = draft.data.find(item => item.id === action.id); if (target) { target[action.property] = action.payload; } }); // 其他case逻辑 } }
4. 局部管理输入值(可选)
对于输入框,先用useState局部保存输入值,在输入结束(如onBlur)时再同步到全局state,避免输入过程中频繁触发全局重渲染:
const PriceItem = React.memo(({ item, onUpdate }) => { const [localPrice1, setLocalPrice1] = React.useState(item.price1); const [localPrice2, setLocalPrice2] = React.useState(item.price2); const [localPrice3, setLocalPrice3] = React.useState(item.price3); const syncToGlobal = () => { onUpdate('price1', localPrice1, item.id); onUpdate('price2', localPrice2, item.id); onUpdate('price3', localPrice3, item.id); }; return ( <div className="price-item"> <input type="number" value={localPrice1} onChange={(e) => setLocalPrice1(e.target.value)} onBlur={syncToGlobal} /> <input type="number" value={localPrice2} onChange={(e) => setLocalPrice2(e.target.value)} onBlur={syncToGlobal} /> <input type="number" value={localPrice3} onChange={(e) => setLocalPrice3(e.target.value)} onBlur={syncToGlobal} /> </div> ); });
内容的提问来源于stack exchange,提问作者Germán
相关产品推荐
相关产品推荐

