【发布时间】:2020-04-23 03:03:20
【问题描述】:
我有一个谜。考虑以下自定义 React 钩子,它按时间段获取数据并将结果存储在 Map 中:
export function useDataByPeriod(dateRanges: PeriodFilter[]) {
const isMounted = useMountedState();
const [data, setData] = useState(
new Map(
dateRanges.map(dateRange => [
dateRange,
makeAsyncIsLoading({ isLoading: false }) as AsyncState<MyData[]>
])
)
);
const updateData = useCallback(
(period: PeriodFilter, asyncState: AsyncState<MyData[]>) => {
const isSafeToSetData = isMounted === undefined || (isMounted !== undefined && isMounted());
if (isSafeToSetData) {
setData(new Map(data.set(period, asyncState)));
}
},
[setData, data, isMounted]
);
useEffect(() => {
if (dateRanges.length === 0) {
return;
}
const loadData = () => {
const client = makeClient();
dateRanges.map(dateRange => {
updateData(dateRange, makeAsyncIsLoading({ isLoading: true }));
return client
.getData(dateRange.dateFrom, dateRange.dateTo)
.then(periodData => {
updateData(dateRange, makeAsyncData(periodData));
})
.catch(error => {
const errorString = `Problem fetching ${dateRange.displayPeriod} (${dateRange.dateFrom} - ${dateRange.dateTo})`;
console.error(errorString, error);
updateData(dateRange, makeAsyncError(errorString));
});
});
};
loadData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dateRanges /*, updateData - for some reason when included this triggers infinite renders */]);
return data;
}
当updateData 作为依赖项添加时,useEffect 被重复触发。如果我将它作为依赖项排除,那么一切都会按预期工作/行为,但eslint 抱怨我违反了react-hooks/exhaustive-deps。
鉴于updateData 一直是useCallback-ed,我不明白为什么它应该反复触发渲染。有没有人能解释一下?
【问题讨论】:
-
你是否尝试从 useCallback 中的依赖数组中移除 setData?
-
鉴于 setData 是一个依赖项,那不就是在不同的地方创建
react-hooks/exhaustive-deps问题吗? -
我认为问题在于“data”变量包含在useCallback的依赖数组中。每次设置数据时,都会更改触发 useCallback 以提供新的 updateData 并触发 useEffect 的数据变量。尝试在不依赖数据变量的情况下实现 updateData。您可以执行类似 setData(d=>new Map(d.set(period, asyncState)) 之类的操作,以避免将“数据”变量传递给 useCallback
-
您还确定 useMountedState 返回相同的 ref 吗?除此之外,我认为@jure 是对的
-
@JohnReilly 很高兴它成功了。我试图撰写一个答案。用文字来解释这种行为并不容易 :) 猜想这是 react 钩子的一个有点令人困惑的部分。
标签: reactjs use-effect usecallback