当我的 useEffect 钩子在 react-apollo 突变后被触发时,如何从反应中解决这个警告?

Posted

技术标签:

【中文标题】当我的 useEffect 钩子在 react-apollo 突变后被触发时,如何从反应中解决这个警告?【英文标题】:How do I resolve this warning from react, when my useEffect hook is triggered after a react-apollo mutation? 【发布时间】:2019-08-31 08:25:48 【问题描述】:

我的 UI 中有一个“卡片”列表,其中在任何给定时间只能展开一张卡片。这个列表有一个 react-apollo 查询,它监视一个作业列表并返回一个标记下一个未完成作业的道具 (nextJobId)。此道具由useEffect 钩子跟踪,当nextJobId 道具更新时,该钩子会更新扩展卡片。

在每张卡片内部都有一个按钮,该按钮会触发 react-apollo 突变,它会更新 DB(将 job.done 设置为 true),进而更新 nextJobId 属性。一旦发生这种情况,包含刚刚单击的按钮的卡片就会折叠,从而破坏按钮。

这似乎触发了以下错误:

react-dom.development.js?61bb:507 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the `componentWillUnmount` method.

我相信正在发生的事情是在突变完成之前正在更新 apollo 缓存(如果我将 onCompleted 函数添加到 Mutation 组件,它永远不会被触发)。

这是钩子:

useEffect(() => 
  client.writeData(
    data: 
      openVisitId: nextJobId
    
  )
, [nextJobId])

这里是突变:

mutation FinishJob($visitId: ID!) 
  updateJobInstance(data:  done: true , where:  id: $visitId ) 
    done
    id
  

到目前为止,我已经想出了两种方法来解决这个问题:

1) 将setTimeout 添加到钩子中:

useEffect(() => 
  setTimeout(() => 
    client.writeData(
      data: 
        liveVisitId: nextJobId,
        openVisitId: nextJobId
      
    )
  )
, [nextJobId])

不知道为什么会这样。

2)从突变中删除done(我相信它绕过了直接缓存更新),并使用refetchQueries再次重新获取主列表:

mutation FinishJob($visitId: ID!) 
  updateJobInstance(data:  done: true , where:  id: $visitId ) 
    id
  

<Mutation mutation=FinishJob refetchQueries=['CardList']>...</Mutation>

这使我认为缓存以某种方式被乐观地更新,这导致单击的按钮在突变实际完成之前被破坏。是这样吗?如果是这样,有什么方法可以告诉react-apollo 在突变真正完成之前不要更新缓存?

【问题讨论】:

好的,将ignoreResults 添加到&lt;Mutation /&gt; 组件似乎可以解决此问题(请参阅apollographql.com/docs/react/essentials/mutations#api)。如果有更好的方法,我不会将其标记为已回答。 【参考方案1】:

此警告的意思是您正在尝试更新未安装组件的状态。例如,您正在等待您的请求完成,然后将数据添加到状态,但是请求花费了很长时间,并且您正在导航到另一个页面。但是请求仍在完成中,所以完成后无法将其数据添加到状态中。

所以为了防止它,最好使用_isMounted

function SomeName()
    const _isMounted = false;

    useEffect(() => 
        _isMounted = true; //this is equivalent of componentDidMoun()
        return () => 
            _isMounted = false; //this is equivalent of componentWillUnmount()
        
    , [])

现在每次您需要更新您的状态时,您都会检查它是否已安装,以便更新状态

if(_isMounted)

    this.setState(
        //your data
    )

【讨论】:

谢谢,有道理!它似乎更多地与子组件上的 react-apollo Mutation 组件有关,而不是父组件上的钩子 - 具有突变的子组件在完成之前被销毁(我假设它正在使用引擎盖下的状态)。

以上是关于当我的 useEffect 钩子在 react-apollo 突变后被触发时,如何从反应中解决这个警告?的主要内容,如果未能解决你的问题,请参考以下文章

useEffect 在自定义钩子中使用 ref 缺少依赖项警告

无效的挂钩调用。钩子只能在反应函数组件内部使用...... useEffect,redux

使用 useEffect 渲染的钩子比上一次渲染时更多

如何在 useEffect 中使用 setTimeout 重置 React 钩子状态

如何将 React 钩子(useContext、useEffect)与 Apollo 反应钩子(useQuery)结合起来

使用 SetTimeout 测试自定义钩子,使用 Jest 测试 useEffect