Apollo 客户端(反应) - 无法更新未安装组件的状态

Posted

技术标签:

【中文标题】Apollo 客户端(反应) - 无法更新未安装组件的状态【英文标题】:Apollo client (react) - Cant update state on unmounted component 【发布时间】:2020-08-28 14:27:48 【问题描述】:

我试图在我的项目中实现社交身份验证并收到此错误:

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。 在 FaceookSignIn(由 Socials 创建) ...

有问题的组件从 facebook 接收代码,该代码被放入 url,用于重定向。

这是路线:

<PublicRoute exact path='/:callback?' component=Auth/>

定义为:

export const PublicRoute = (component: Component, ...rest) => 
  const client, loading, data = useQuery(GET_USER, fetchPolicy: 'cache-only')
  let isAuthenticated = !!data.user.accessToken

  return (
    <Route ...rest component=(props)=>(
      isAuthenticated ? (
        <Redirect to='/home' />
      ) : (
        <Component ...props />
      )
    )/>
  )  

我尝试在我的组件上使用钩子清理,但错误仍然存​​在。这是我当前的实现的样子:

const FaceookSignIn = () => 
  let _isMounted = false
  const client = useApolloClient()
  const appId = '187856148967924'
  const redirectUrl = `$document.location.protocol//$document.location.host/facebook-callback`;

  const code = (document.location.pathname === '/facebook-callback') ? querystring.parse(document.location.search)['?code'] : null
  const [loading, setLoading] = useState(false)
  const [callFacebook, data] = useMutation(FACEBOOK_SIGN_IN)

  useEffect(()=>
    _isMounted = true
    if(!code) return
    if(_isMounted) callFacebook(variables: code: code)
    .then(res=>
      const error, name, email, accessToken = res.data.facebookSignIn
      if (error) 
        alert(`Sign in error: $error`);
       else 
        client.writeData(
          data: 
            user: 
              name: name,
              email: email,
              accessToken: accessToken,
              __typename: 'User'
            
          
        )
        setLoading(false)
      
    )
    .catch(e=>
      console.log(e)
      setLoading(false)
    )
    return ()=> _isMounted = false
  ,[])

  const handleClick = e => 
    setLoading(true)
    e.preventDefault()
    window.location.href = `https://www.facebook.com/v2.9/dialog/oauth?client_id=$appId&redirect_uri=$encodeURIComponent(redirectUrl)`;
  

  return (
    <a className="login-options__link" href='/facebook-login' onClick=handleClick>
      loading ? <p>loading...</p> : <img className="social-link__icon" src=fb.default id="facebook" /> 
    </a>
  )

这种方法有些工作,加载凭据并将用户重定向到经过身份验证的路由,但控制台仍会抛出该错误,并且 ui 有时会在路由之间闪烁。我在这上面花了两天时间,我没有想法。我错过了什么明显的东西吗?

【问题讨论】:

_isMounted 在这里无效。它不会告诉您您的异步操作callFacebook 是否在 组件被卸载后解析。不要使用_isMounted,而是在您的清理处理程序中取消callFacebook 请求 - 这样您就不会在组件卸载后调用setLoading(false) 感谢您的回复,但setLoading 不是这里的罪魁祸首,即使在完全删除它之后我收到同样的错误。我也在印象中阿波罗查询在卸载时会自动中止。无论如何我都会尝试取消它并返回 【参考方案1】:

好吧,终于想通了,事实证明我不应该从 apollo 钩子中劫持逻辑并小心处理数据的方式。我假设突变挂钩会自行更新客户端状态,.then() 块在客户端更新和卸载组件之前解决。也许有人可以澄清一下?

如果有人感兴趣,这里是更新的代码:

const FaceookSignIn = (props) => 
  const appId = '187856148967924'
  const redirectUrl = `$document.location.protocol//$document.location.host/facebook-callback`
  const code = (document.location.pathname === '/facebook-callback') ? querystring.parse(document.location.search)['?code'] : null

//moved data handling logic to hooks optional callback
const [callFacebook, client, data, loading, error, called] = useMutation(FACEBOOK_SIGN_IN, onCompleted: (data)=>
    const name, email, accessToken = data.facebookSignIn
    client.writeData(
      data: 
        user: 
          name: name,
          email: email,
          accessToken: accessToken,
          __typename: 'User'
        
      
    )
  )

  if(code && !called) 
    callFacebook(variables: code: code)
  

  const handleClick = e => 
    e.preventDefault()
    window.location.href = `https://www.facebook.com/v2.9/dialog/oauth?client_id=$appId&redirect_uri=$encodeURIComponent(redirectUrl)`;
  

  return (
    <a className="login-options__link" href='/facebook-login' onClick=handleClick>
      loading ? <p>loading...</p> : <img className="social-link__icon" src=fb.default id="facebook" /> 
    </a>
  )

【讨论】:

以上是关于Apollo 客户端(反应) - 无法更新未安装组件的状态的主要内容,如果未能解决你的问题,请参考以下文章

Apollo 客户端:缓存更新后组件不呈现(反应变量)

@apollo/client 反应钩子 useQuery() 数据未定义

Apollo 客户端 devtool 无法在反应本机应用程序中检测到 Apollo 客户端

反应钩子。无法对未安装的组件执行 React 状态更新

无法对未安装的组件执行 React 状态更新(useEffect 反应挂钩)

重新获取后 apollo graphql UI 未更新