React Hooks:在 useEffect 钩子中引用上下文和管理依赖项
React Hooks: Referencing context and managing dependencies in useEffect hook
我正在尝试使用反应挂钩制作一个 Table 组件,该组件根据用户可以选择的一组过滤器显示来自 API 的数据行。每当用户单击 'Apply Filters' 按钮时,我想重新调用以获取数据,当用户更改过滤器时 而不是 。
我正在使用上下文来管理 'filters' 状态和跟踪用户最后一次单击 'Apply Filters' 按钮的时间的 'lastFetched' 状态(以及页面上的其他状态) .上下文的更新是通过 useReducer 挂钩及其调度方法进行的(参见 here)。
数据获取发生在 useEffect 挂钩中,只要 'lastFetched' 状态发生变化,该挂钩就会重新运行。这似乎工作正常;然而,效果引用了上下文(即过滤器)中未包含在依赖项中的其他值。我知道 exhaustive-deps eslint 规则,我担心我没有正确处理钩子的依赖项。
const Table = () => {
const [context, dispatch] = useTableContext(); // implemented with createContext and useReducer
const { filters, lastFetched } = context;
useEffect(() => {
if (!filters.run) {
return;
}
dispatch({ type: 'FETCH_DATA_BEGIN' });
const params = convertContextToParams(context); // this is lazy, but essentially just uses the the filters and some other state from the context
API.fetchData(params)
.then((data) => {
dispatch({ type: 'FETCH_DATA_SUCCESS', payload: data.results });
})
.catch((e) => {
dispatch({ type: 'FETCH_DATA_FAILURE', payload: e.response.data.message });
});
return () => { ... some cleanup... };
}, [lastFetched]); // <== This is the part in question
return <...some jsx.../>
};
同样,这似乎有效,但根据反应文档,似乎我应该将钩子中使用的上下文中的所有值包括在钩子的依赖项中,以防止引用过时。这会导致逻辑中断,因为我不想在过滤器更改时获取数据。
我的问题是:当用户单击 'Apply Filters'、更新 context.lastFetched 并触发 useEffect 挂钩时,该挂钩是否会从上下文中引用陈旧的过滤器状态?如果是这样,为什么?由于每当单击按钮时都会重新运行效果,并且所有状态更新都是通过 reducer 完成的,因此在闭包中引用陈旧变量的常见危险是否仍然适用?
感谢任何指导!
注意:我考虑过使用 useRef 来防止这个问题,或者可能设计一些自定义异步中间件来获取特定调度的数据,但这是我目前的解决方案。
我不是专家,但我想提供我的看法。根据我对 Context 工作原理的理解,您不会在当前实现中获得过时的过滤器数据。 useReducer
使用新对象更新状态,这将触发 Table
重新渲染。
此外,Table
组件并不真正关心过滤器数据,除非 lastFetched
被单击事件更改。如果 lastFetched
发生变化, TableContext
的所有 Consumer 将重新渲染。您也不应该获得过时的过滤器数据。
我正在尝试使用反应挂钩制作一个 Table 组件,该组件根据用户可以选择的一组过滤器显示来自 API 的数据行。每当用户单击 'Apply Filters' 按钮时,我想重新调用以获取数据,当用户更改过滤器时 而不是 。
我正在使用上下文来管理 'filters' 状态和跟踪用户最后一次单击 'Apply Filters' 按钮的时间的 'lastFetched' 状态(以及页面上的其他状态) .上下文的更新是通过 useReducer 挂钩及其调度方法进行的(参见 here)。
数据获取发生在 useEffect 挂钩中,只要 'lastFetched' 状态发生变化,该挂钩就会重新运行。这似乎工作正常;然而,效果引用了上下文(即过滤器)中未包含在依赖项中的其他值。我知道 exhaustive-deps eslint 规则,我担心我没有正确处理钩子的依赖项。
const Table = () => {
const [context, dispatch] = useTableContext(); // implemented with createContext and useReducer
const { filters, lastFetched } = context;
useEffect(() => {
if (!filters.run) {
return;
}
dispatch({ type: 'FETCH_DATA_BEGIN' });
const params = convertContextToParams(context); // this is lazy, but essentially just uses the the filters and some other state from the context
API.fetchData(params)
.then((data) => {
dispatch({ type: 'FETCH_DATA_SUCCESS', payload: data.results });
})
.catch((e) => {
dispatch({ type: 'FETCH_DATA_FAILURE', payload: e.response.data.message });
});
return () => { ... some cleanup... };
}, [lastFetched]); // <== This is the part in question
return <...some jsx.../>
};
同样,这似乎有效,但根据反应文档,似乎我应该将钩子中使用的上下文中的所有值包括在钩子的依赖项中,以防止引用过时。这会导致逻辑中断,因为我不想在过滤器更改时获取数据。
我的问题是:当用户单击 'Apply Filters'、更新 context.lastFetched 并触发 useEffect 挂钩时,该挂钩是否会从上下文中引用陈旧的过滤器状态?如果是这样,为什么?由于每当单击按钮时都会重新运行效果,并且所有状态更新都是通过 reducer 完成的,因此在闭包中引用陈旧变量的常见危险是否仍然适用?
感谢任何指导!
注意:我考虑过使用 useRef 来防止这个问题,或者可能设计一些自定义异步中间件来获取特定调度的数据,但这是我目前的解决方案。
我不是专家,但我想提供我的看法。根据我对 Context 工作原理的理解,您不会在当前实现中获得过时的过滤器数据。 useReducer
使用新对象更新状态,这将触发 Table
重新渲染。
此外,Table
组件并不真正关心过滤器数据,除非 lastFetched
被单击事件更改。如果 lastFetched
发生变化, TableContext
的所有 Consumer 将重新渲染。您也不应该获得过时的过滤器数据。