React自定义useHandleData Hook陷入无限循环问题求助
React自定义Hook useHandleData无限循环问题排查与解决
我创建了一个名为useHandleData的自定义React Hook,用于根据searchQuery、filters、searchKeys对传入的数据执行搜索和过滤,但Hook中的useEffect陷入了无限循环,相关代码如下:
Hook代码
import React, { useEffect, useState } from 'react'; type PropsType<T> = { searchQuery?: string; filters?: Array<{ filter: string, filterKey: keyof T }>; searchKeys?: (keyof T)[]; } & ( { searchQuery: string, searchKeys: (keyof T)[] } | { filters: Array<{ filter: string, filterKey: keyof T }> } ) const useHandleData = <T>(props: PropsType<T>, data: Array<T>) => { const { searchQuery, filters = [], searchKeys = [] } = props; const [displayData, setDisplayData] = useState<Array<T>>([]); useEffect(() => { let filteredData = data; if (searchQuery && searchKeys.length > 0) { filteredData = searchThroughData(filteredData, searchQuery, searchKeys); } if (filters.length > 0) { filters.forEach(filter => { filteredData = filterThroughData(filteredData, filter.filter, filter.filterKey); }); } setDisplayData(filteredData); }, [searchQuery, filters, data]); return { displayData }; } const searchThroughData = <T>(data: Array<T>, searchQuery: string, searchKeys: (keyof T)[]) => { if (!searchQuery || searchKeys.length === 0) return data; return data.filter(item => { for (let key of searchKeys) { if (item[key]?.toString().toLowerCase().includes(searchQuery.toString().toLowerCase())) return true; } return false; }); } const filterThroughData = <T>(data: Array<T>, filter: string, filterKey: keyof T) => { if (!filter || !filterKey) return data; return data.filter(item => item[filterKey] === filter); } export { useHandleData };
调用代码
const [searchQuery, setSearchQuery] = useState("") const [filterQuery, setFilterQuery] = useState<{ filter: string, filterKey: keyof item }[]>([]) const { displayData } = useHandleData({ searchQuery: searchQuery, searchKeys: ['name', 'itemName'], filters: filterQuery }, itemList)
问题原因
无限循环的核心是**useEffect依赖项的引用不稳定**,具体包括:
data参数:如果父组件传入的itemList不是通过useState或useMemo缓存的,每次组件渲染都会生成新的数组实例,导致data依赖项每次变化,触发useEffect重复执行。searchKeys参数:调用Hook时直接传入['name', 'itemName'],每次渲染都会创建新数组,且useEffect遗漏了searchKeys依赖,导致逻辑依赖不稳定。filters参数:如果父组件中filterQuery的更新或定义方式导致每次生成新数组,也会触发useEffect重复执行。- 状态更新循环:
useEffect中调用setDisplayData会触发组件重新渲染,若依赖项持续变化,就会形成无限循环。
解决方案
1. 稳定data引用
在父组件中用useMemo缓存动态生成的itemList,确保其引用稳定:
// 父组件 const itemList = useMemo(() => { // 这里是生成数据的逻辑,比如从API获取或本地计算 return [/* 你的数据数组 */]; }, []); // 根据实际依赖添加,比如API请求参数等
2. 稳定searchKeys引用
用useMemo缓存searchKeys,避免每次渲染创建新数组:
// 父组件 const searchKeys = useMemo(() => ['name', 'itemName'], []); const { displayData } = useHandleData({ searchQuery: searchQuery, searchKeys, filters: filterQuery }, itemList);
3. 用useMemo替代useEffect+useState
直接通过useMemo计算过滤结果,省去额外的状态更新,更高效也避免循环:
const useHandleData = <T>(props: PropsType<T>, data: Array<T>) => { const { searchQuery, filters = [], searchKeys = [] } = props; const displayData = useMemo(() => { let filteredData = [...data]; // 可选:复制原数组避免直接修改 if (searchQuery && searchKeys.length > 0) { filteredData = searchThroughData(filteredData, searchQuery, searchKeys); } if (filters.length > 0) { filters.forEach(filter => { filteredData = filterThroughData(filteredData, filter.filter, filter.filterKey); }); } return filteredData; }, [searchQuery, filters, searchKeys, data]); return { displayData }; };
4. 确保filters引用稳定
更新filterQuery时,使用函数式更新确保仅在必要时创建新数组:
// 父组件中添加过滤条件的示例 const addFilter = (newFilter) => { setFilterQuery(prevFilters => [...prevFilters, newFilter]); };
额外优化
如果filters中的对象是复杂类型,浅比较无法识别内容变化,可以自定义深比较的useMemo,或者使用第三方库(如use-deep-compare-effect)来避免不必要的重新计算。
内容的提问来源于stack exchange,提问作者Rohan P Suresh
相关产品推荐
相关产品推荐

