React过滤10k+对象时输入卡顿,useCallback无效求助
解决React子组件处理大量数据时输入卡顿的问题
问题根源
你现在的卡顿不是组件重渲染导致的,而是每次输入都同步遍历10000+条数据执行过滤操作,大量计算阻塞了主线程——useCallback只能缓存函数,避免不必要的组件重渲染,解决不了计算量过大的核心问题。
优化方案
1. 给过滤逻辑加防抖
用户输入时,不要每打一个字符就立刻过滤,等输入停止一段时间(比如300ms)再执行,大幅减少过滤触发次数:
// 抽成工具函数复用 const debounce = (func, delay) => { let timeoutId; return (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; };
2. 提前预处理数据
把员工的firstName提前转成小写存好,避免每次过滤都重复执行toLowerCase(),减少重复计算:
在父组件拿到API数据后直接预处理:
useEffect(() => { const fetchData = async () => { const rawList = await fetchEmployees(); // 你的API请求逻辑 // 预处理:新增小写名字字段,后续过滤直接用 const processedList = rawList.map(staff => ({ ...staff, lowerFirstName: staff.firstName.toLowerCase() })); setEmployeeList(processedList); }; fetchData(); }, []);
3. 优化useCallback依赖
原来的useCallback依赖userInput,导致每次输入都会重新创建函数,改成依赖staffList,再结合防抖逻辑:
优化后的子组件代码
import { useState, useCallback, useEffect } from 'react'; const debounce = (func, delay) => { let timeoutId; const debouncedFunc = (...args) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; // 增加取消方法,组件卸载时清除定时器 debouncedFunc.cancel = () => clearTimeout(timeoutId); return debouncedFunc; }; const Staff = ({ EmployeeList: staffList }) => { const [userInput, setUserInput] = useState(''); const [filteredSuggestions, setFilteredSuggestions] = useState([]); // 防抖后的过滤逻辑 const debouncedFilter = useCallback(debounce((input) => { if (!staffList) return; const filtered = staffList.filter(staff => staff.lowerFirstName.includes(input.toLowerCase()) ); setFilteredSuggestions(filtered); }, 300), [staffList]); const onChange = useCallback((e) => { const inputValue = e.currentTarget.value; setUserInput(inputValue); debouncedFilter(inputValue); }, [debouncedFilter]); // 组件卸载时清除防抖定时器,避免内存泄漏 useEffect(() => { return () => debouncedFilter.cancel(); }, [debouncedFilter]); return ( <> <input value={userInput} onChange={onChange} placeholder="搜索员工..." /> {/* 如果过滤结果仍很多,建议用react-window等虚拟列表库,只渲染可见区域的项 */} <ul> {filteredSuggestions.map(staff => ( <li key={`${staff.firstName}-${staff.lastName}`}> {staff.firstName} {staff.lastName} </li> ))} </ul> </> ); }; export default Staff;
4. 额外优化:虚拟列表
如果过滤后的结果仍然很多,渲染大量DOM节点也会导致卡顿,建议使用react-window这类虚拟列表库,只渲染当前可见区域的列表项,大幅减少DOM操作开销。
内容的提问来源于stack exchange,提问作者Honey Singh
相关产品推荐
相关产品推荐

