使用useEffect的清理操作超级轻松地取消请求
useEffect(() => {
let ignore = false;
fetchData(id).then(data => {
if (!ignore) {
setData(data);
}
});
return () => (ignore = true);
}, [id]);
我想做类似的事情,但我需要使用useInterval
来轮询数据我想用fetchData(id)
轮询数据,如果请求已触发但id
在响应解析之前发生了变化,则忽略返回的响应。
答案 0 :(得分:0)
这是我想出的useInterval
function (callback, options, cleanup) {
const savedCallback = useRef();
// Remember the latest callback.
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
// Set up the interval.
useEffect(() => {
function tick() {
savedCallback.current();
}
if (options.delay !== null) {
let id = setInterval(tick, options.delay);
return () => {
clearInterval(id);
cleanup && cleanup();
}
}
return () => cleanup && cleanup();
}, [options]);
}
我这样使用
const [fetchOptions, setFetchOptions] = useState({delay: 5000, id: 'someid'});
let ignore = false;
useInterval(
() => {
fetchData(fetchOptions.id).then(data => {
if (!ignore) {
setData(data);
}
});
},
fetchOptions,
() => (ignore = true),
);
我不确定是否有更好的方法来编写此代码。我关心的是ignore
变量的作用范围是功能/组件的上下文。在问题的代码示例中,ignore变量位于useEffect内,感觉更干净。
这种方法的缺点是fetchOptions必须是一个useState变量,否则,如果它只是函数中的一个常量,它将在每个渲染器上重置useInterval钩子。
答案 1 :(得分:0)
假设您收到id
作为道具或类似物品,这行得通吗?
const Foo = ({ id }) => {
const [data, setData] = useState()
const passedId = useRef()
passedId.current = id
useInterval(() => {
fetchData(id)
.then(response => id === passedId.current && setData(response))
}, 5000)
// Render some JSX with the data
我在本地测试了一些非常类似的东西,本质上是这样的:
Foo
收到id = 6
6
存储在passedId
useInterval
滴答,我们要求id = 6
id = 7
7
存储在passedId
id = 6
的请求已完成,但是passedId.current === 7
使得setData
未被调用id = 7
的请求已完成,id === passedId.current
和setData
被调用useInterval
取消效果如此简单的原因可能是因为该函数返回了自己的清理功能,因此ignore
不必在外部范围内。但是setInterval
不允许返回值。我们可能可以使用setTimeout
来解决此问题:
function useInterval(callback, delay) {
useEffect(() => {
let cleanup
function tick() {
cleanup = callback()
return setTimeout(tick, delay)
}
if (delay !== null ) {
const id = tick()
return () => {
clearTimeout(id)
cleanup && cleanup()
}
}
return () => cleanup && cleanup()
}, [callback, delay])
与此相关的一个问题是,现在我们在依赖项数组中有callback
,因此应使用useInterval
创建给useCallback
的回调:
const Foo = ({ id }) => {
const [data, setData] = useState()
const pollData = useCallback(() => {
let shouldUpdate = true
fetchData(id).then(resp => shouldUpdate && setData(resp))
return () => shouldUpdate = false
}, [id])
useInterval(pollData, 5000)
// Render some JSX with the data
useInterval
时,我们设置了一个执行useEffect
的{{1}} callback
时,返回值(对其进行的清理)存储在callback
中,其作用范围是效果,并且将cleanup
设置为在{{ 1}}毫秒setTimeout
发生了变化(即,我们得到了一个新的delay
),那么我们将清除超时并为先前的回调运行清除操作callback
毫秒后,id
被再次执行