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/client 反应钩子 useQuery() 数据未定义
Apollo 客户端 devtool 无法在反应本机应用程序中检测到 Apollo 客户端