确认React自定义Hook仅在指定变量变更时执行的实现方式
自定义Hook触发时机问题及修正方案
问题场景
维护代码时遇到如下自定义Hook:
// 组件中调用 const { data, loading, count, mutate } = useSomeoneHook(filter, page, pageSize); // Hook实现 const useSomeoneHook = (filter, page, pageSize) => { const { data, error, mutate } = fetch(filter, page, pageSize); return { filter: data, count: data.count, loading: !error && !data, mutate }; };
原本预期该Hook仅在filter、page、pageSize变更时执行,但调试发现组件其他状态(如调用setSomeVariable(false))更新时,Hook也会触发执行。
核心问题原因
React组件每次状态更新都会触发重渲染,而自定义Hook会在组件重渲染时重新执行一次。原Hook中直接调用fetch,导致每次组件重渲染都会触发fetch请求,不管传入的filter、page、pageSize有没有变化。
解决方案:用useEffect限制执行时机
是的,必须在自定义Hook内部用useEffect包裹请求逻辑,并将filter、page、pageSize放入依赖数组,才能让请求仅在这些变量变更时执行。
不过你给出的修正写法存在几个问题,需要调整:
- 若
fetch是异步函数,直接赋值result会得到Promise对象,无法直接获取data、error等属性 - 初始
data为空对象,直接访问data.data会返回undefined,继续访问count会抛出报错 loading逻辑依赖data.error和data.data,初始状态下逻辑不成立mutate直接取data.mutate,初始状态下会因data为空导致报错
正确的修正示例
const useSomeoneHook = (filter, page, pageSize) => { const [data, setData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const fetchData = async () => { setLoading(true); try { // 假设fetch是返回{ data, error, mutate }的异步函数 const result = await fetch(filter, page, pageSize); setData(result.data); setError(result.error); } catch (err) { setError(err); setData(null); } finally { setLoading(false); } }; fetchData(); }, [filter, page, pageSize]); // 封装mutate函数,确保依赖变化时更新 const mutate = useCallback(async () => { setLoading(true); try { const result = await fetch(filter, page, pageSize); setData(result.data); setError(result.error); } catch (err) { setError(err); setData(null); } finally { setLoading(false); } }, [filter, page, pageSize]); return { data, count: data?.count || 0, loading, error, mutate }; };
关键调整点说明
- 用
useEffect包裹请求逻辑,依赖数组仅包含filter、page、pageSize,确保只有这些变量变化时才触发请求 - 单独维护
loading、error状态,避免依赖data的存在性判断导致逻辑混乱 - 处理异步请求的
await和错误捕获,避免将Promise对象存入state - 用
useCallback包裹mutate函数,确保依赖变化时更新函数引用,避免闭包问题 - 使用可选链
?.访问data.count,并设置默认值0,避免初始状态或数据为空时的报错
内容的提问来源于stack exchange,提问作者Patryk
相关产品推荐
相关产品推荐

