如何清理 useEffect 中的 Redux“useDispatch”操作?

Posted

技术标签:

【中文标题】如何清理 useEffect 中的 Redux“useDispatch”操作?【英文标题】:How to clean up a Redux "useDispatch" action inside useEffect? 【发布时间】:2021-01-04 17:51:34 【问题描述】:

我找到了很多这方面的例子,但没有一个使用 Redux 和useDispatch,所以情况是这样的......

我有一个带有像这样的异步操作的操作文件...

//postsActions.tsx

export const asyncGetPostById = (postId: string) => 
    return async (dispatch: any, getState: () => AppState) => 
        try 
            dispatch(startLoader());
            const  data  = await axios.get(`api/posts/$postId`);
            dispatch(setPostDataInReducer(data));
         catch (error) 
            console.log(error)
        
    ;
;

我在 Post.tsx 组件中调用这个动作,就像这样......

const dispatch = useDispatch();

useEffect(() => 
    dispatch(asyncGetPostById(postId));
, []);

这当然是一个简化的版本,但它的想法是一样的。现在我怎样才能清理像useEffect 这样的调度以避免这个错误......

警告:无法对未安装的组件执行 React 状态更新。 这是一个空操作,但它表明您的应用程序中存在内存泄漏。 要修复,请取消 useEffect 中的所有订阅和异步任务 清理功能。

编辑

这个想法是如何取消 ComponentWillUnmount 上的请求(或者在我的情况下是 useEffect 中的清理功能),如果它还没有完成,这样你就不会在后台有过时或未解决的请求,这将使你的应用程序慢。

这是一个非常常见的问题,很多人都成为受害者,并且有很多解决方案,但不适用于这种特殊情况(使用 dispatch 和 redux)。

有些使用AbortController(),有些使用axios.CancelToken,但我不知道在我的情况下如何使用它们。

【问题讨论】:

axios.get 解决时组件可能已卸载的问题?我能想到一个丑陋的解决方案(传入一个在卸载时发生突变的对象),希望有更好的解决方案 是的,这就是问题所在,有些使用axios.CancelToken,有些使用AbortController,但我不知道在这种情况下如何在dispatch和redux中使用这些。 这个想法是如何取消 ComponentWillUnmount 上的请求,如果它还没有完成,这样您就不会在后台有陈旧或未解决的请求,这会使您的应用程序变慢。 这不应该引发错误,除非您在请求完成后设置要声明的内容。但是,如果您想取消请求,那么您将使用您提到的选项之一(尽管AbortController 在某些浏览器中不起作用)。另一种方法是使用 redux sagas 而不是 thunk,它允许您手动取消任务。也就是说,如果没有 repo 或工作演示,就很难推荐最佳方法。 @MattCarlotta 我确实在请求之后更改了状态,或者更确切地说是减速器,在某些情况下,我调度了另一个异步操作,它也更新了减速器。我只需要知道如何取消上述示例中的请求即可。 【参考方案1】:

为你的动作创建者试试这个:

export const asyncGetPostById = (postId: string) => async (dispatch: Function, getState: () => AppState) => 
    try 
        dispatch(startLoader());
        const  data  = await axios.get(`api/posts/$postId`);
        dispatch(setPostDataInReducer(data));
     catch (error) 
        console.log(error)
    
;

这是一个微妙的变化,但是使用这个动作创建器,你实际上并没有返回任何东西(在 TypeScript 中它应该是 void

使用void 返回,您在动作创建器中的dispatch 调用将直接转到您的reducer,但在您的组件中不会返回任何内容,因此您不必清理任何内容。

编辑:

此外,您应该考虑将dispatch 添加到您的useEffects 依赖数组中:

const dispatch = useDispatch();

useEffect(() => 
    dispatch(asyncGetPostById(postId));
, [dispatch]);

【讨论】:

好吧,我发现我遇到的问题是因为我在调度完成之前关闭了弹出窗口,所以我所要做的就是在弹出窗口中创建 async-await 函数并等待调度然后关闭弹出窗口,它工作。但是,我仍然想知道如何清理 useEffect 中的调度。 很高兴您解决了这个问题。据我所知,您不需要清理 redux 调度;您没有使用它进行任何订阅,因此您不必清理任何东西。【参考方案2】:

你必须在你的 reducer 中创建一个 case 来清理当前状态。然后在useEffect 中你会这样做

React.useEffect(()=> 
 dispatch(your_action)
 return () => dispatch(clean_data_action)
,[])
 

【讨论】:

不是这样,不是。 根据本次讨论:github.com/facebook/react/issues/14285,您似乎无法从 useEffect 清理函数中调度操作 我在一个项目中做了这个。您可以在清理功能上调度操作 你能解释一下@Ruby 为什么不是这样吗?我也有类似的问题

以上是关于如何清理 useEffect 中的 Redux“useDispatch”操作?的主要内容,如果未能解决你的问题,请参考以下文章

如何取消 useEffect 清理函数中的所有订阅和异步任务?

useEffect TypeScript 错误中的 Redux 操作?

如何在页面加载时调度 redux 操作以在 useEffect 中加载数据

使用 Redux Thunk 时正确理解 useEffect 中的依赖数组

如何使用 React hooks 和 Redux 从 useEffect 执行 store.unsubscribe

react 钩子中的 useEffect 执行顺序及其内部清理逻辑是啥?