无法使用上下文提供程序对未安装的组件执行 React 状态更新
Can't perform a React state update on an unmounted component using context provider
对于 React 项目,我使用多个上下文提供程序 运行 GraphQL 查询,为所有或部分组件提供我需要的数据。对于这个例子,我有一个 BookProvider 查询特定用户的所有书籍。
export const BookProvider = ({ children }) => {
// GraphQL query for retrieving the user-belonged books
const { loading, data } = useQuery(gql`{
books {
id,
name,
description
}
}
`, {
pollInterval: 500
})
if(!data?.books) return null
return (
<BookContext.Provider value={data?.books}>
{!loading && children}
</BookContext.Provider>
)
}
我确保查询未加载并且在 data
对象中可用。为了检索我的数据,我有一个包含在该提供程序中的仪表板组件。在这里,还有其他组件在加载相应的数据。
const Dashboard = () => {
return (
<BookProvider>
// More components for loading data
</BookProvider>
)
}
数据是这样加载的:
const { id, name } = useContext(BookContext)
以同样的方式,我有一个AuthenticationProvider
,包装整个应用程序。
export const MeQuery = gql`{ me { id, email } }`
export const AuthenticationProvider = (props) => {
const { loading, data } = useQuery(MeQuery)
if(loading) return null
const value = {
user: (data && data.me) || null
}
return <AuthenticationContext.Provider value={value} {...props} />
}
对于我的应用程序路由,检查用户是否已通过身份验证,我使用 PrivateRoute
组件。 Dashboard
组件由此 PrivateRoute
.
加载
const PrivateRoute = ({ component: Component, ...rest }) => {
const { user } = useAuthentication()
return user ? <Route {...rest} render={props => <Component {...props} />} /> : <Redirect to={{ pathname: '/login' }} />
}
登录并设置用户对象时没有问题,但是,当注销时,它是 Dashboard
上的一个组件,它会将我重定向到登录页面,但我收到以下错误:
index.js:1 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 a useEffect cleanup function.
at BookProvider (http://localhost:3000/static/js/main.chunk.js:2869:3)
at Dashboard
at Route (http://localhost:3000/static/js/vendors~main.chunk.js:85417:29)
at PrivateRoute (http://localhost:3000/static/js/main.chunk.js:4180:14)
at Switch (http://localhost:3000/static/js/vendors~main.chunk.js:85619:29)
at Router (http://localhost:3000/static/js/vendors~main.chunk.js:85052:30)
at BrowserRouter (http://localhost:3000/static/js/vendors~main.chunk.js:84672:35)
at Router
为了注销,我使用了以下代码:
export function useLogout() {
const client = useApolloClient()
const logout = useMutation(gql`mutation Logout { logout }`, { update: async cache => {
cache.writeQuery({ query: MeQuery, data: { me: null }})
await client.resetStore()
}})
return logout
}
export default useLogout
这被称为 const [logout] = useLogout()
问题
- 为什么会出现这个错误?
- 如何解决这个问题?
- 在上下文提供程序中查询 运行 是一种好习惯吗?
您的 graphql 查询挂钩似乎正在轮询数据,这将更新其内部状态,或者尝试这样做,即使组件已卸载。
我读过类似的问题 in that thread(它看起来像一个错误,因为 useQuery
挂钩应该检测何时卸载组件并停止轮询)
似乎适合您的问题的解决方案之一是控制轮询,以便您可以在组件卸载后停止它。
看起来像这样:
export const BookProvider = ({ children }) => {
// GraphQL query for retrieving the user-belonged books
const { loading, data, stopPolling, startPolling } = useQuery(gql`{
books {
id,
name,
description
}
}`, {fetchPolicy: "network-only"})
React.useEffect(function managePolling() {
startPolling(500)
return () => stopPolling()
})
if(!data?.books) return null
return (
<BookContext.Provider value={data?.books}>
{!loading && children}
</BookContext.Provider>
)
}
它是否抑制警告?
注:不确定是否需要{fetchPolicy: "network-only"}
选项
我再试一次;-)
您可以改用 useLazyQuery
,这似乎可以避免困扰 useQuery
:
的错误
const [ executeQuery, { data, loading } ] = useLazyQuery(gql`${QUERY}`);
然后手动管理轮询:
useEffect(function managePolling() {
const timeout = null
const schedulePolling = () => {
timeout = setTimeout(() => {
executeQuery()
schedulePolling()
}, 500)
}
executeQuery()
schedulePolling()
return () => clearTimeout(timeout)
}, [executeQuery])
(未经测试的代码,但你明白了)
这是在同一主题 (https://github.com/apollographql/apollo-client/issues/6209#issuecomment-676373050) 上提出的建议,所以也许您已经尝试过...
对于 React 项目,我使用多个上下文提供程序 运行 GraphQL 查询,为所有或部分组件提供我需要的数据。对于这个例子,我有一个 BookProvider 查询特定用户的所有书籍。
export const BookProvider = ({ children }) => {
// GraphQL query for retrieving the user-belonged books
const { loading, data } = useQuery(gql`{
books {
id,
name,
description
}
}
`, {
pollInterval: 500
})
if(!data?.books) return null
return (
<BookContext.Provider value={data?.books}>
{!loading && children}
</BookContext.Provider>
)
}
我确保查询未加载并且在 data
对象中可用。为了检索我的数据,我有一个包含在该提供程序中的仪表板组件。在这里,还有其他组件在加载相应的数据。
const Dashboard = () => {
return (
<BookProvider>
// More components for loading data
</BookProvider>
)
}
数据是这样加载的:
const { id, name } = useContext(BookContext)
以同样的方式,我有一个AuthenticationProvider
,包装整个应用程序。
export const MeQuery = gql`{ me { id, email } }`
export const AuthenticationProvider = (props) => {
const { loading, data } = useQuery(MeQuery)
if(loading) return null
const value = {
user: (data && data.me) || null
}
return <AuthenticationContext.Provider value={value} {...props} />
}
对于我的应用程序路由,检查用户是否已通过身份验证,我使用 PrivateRoute
组件。 Dashboard
组件由此 PrivateRoute
.
const PrivateRoute = ({ component: Component, ...rest }) => {
const { user } = useAuthentication()
return user ? <Route {...rest} render={props => <Component {...props} />} /> : <Redirect to={{ pathname: '/login' }} />
}
登录并设置用户对象时没有问题,但是,当注销时,它是 Dashboard
上的一个组件,它会将我重定向到登录页面,但我收到以下错误:
index.js:1 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 a useEffect cleanup function.
at BookProvider (http://localhost:3000/static/js/main.chunk.js:2869:3)
at Dashboard
at Route (http://localhost:3000/static/js/vendors~main.chunk.js:85417:29)
at PrivateRoute (http://localhost:3000/static/js/main.chunk.js:4180:14)
at Switch (http://localhost:3000/static/js/vendors~main.chunk.js:85619:29)
at Router (http://localhost:3000/static/js/vendors~main.chunk.js:85052:30)
at BrowserRouter (http://localhost:3000/static/js/vendors~main.chunk.js:84672:35)
at Router
为了注销,我使用了以下代码:
export function useLogout() {
const client = useApolloClient()
const logout = useMutation(gql`mutation Logout { logout }`, { update: async cache => {
cache.writeQuery({ query: MeQuery, data: { me: null }})
await client.resetStore()
}})
return logout
}
export default useLogout
这被称为 const [logout] = useLogout()
问题
- 为什么会出现这个错误?
- 如何解决这个问题?
- 在上下文提供程序中查询 运行 是一种好习惯吗?
您的 graphql 查询挂钩似乎正在轮询数据,这将更新其内部状态,或者尝试这样做,即使组件已卸载。
我读过类似的问题 in that thread(它看起来像一个错误,因为 useQuery
挂钩应该检测何时卸载组件并停止轮询)
似乎适合您的问题的解决方案之一是控制轮询,以便您可以在组件卸载后停止它。
看起来像这样:
export const BookProvider = ({ children }) => {
// GraphQL query for retrieving the user-belonged books
const { loading, data, stopPolling, startPolling } = useQuery(gql`{
books {
id,
name,
description
}
}`, {fetchPolicy: "network-only"})
React.useEffect(function managePolling() {
startPolling(500)
return () => stopPolling()
})
if(!data?.books) return null
return (
<BookContext.Provider value={data?.books}>
{!loading && children}
</BookContext.Provider>
)
}
它是否抑制警告?
注:不确定是否需要{fetchPolicy: "network-only"}
选项
我再试一次;-)
您可以改用 useLazyQuery
,这似乎可以避免困扰 useQuery
:
const [ executeQuery, { data, loading } ] = useLazyQuery(gql`${QUERY}`);
然后手动管理轮询:
useEffect(function managePolling() {
const timeout = null
const schedulePolling = () => {
timeout = setTimeout(() => {
executeQuery()
schedulePolling()
}, 500)
}
executeQuery()
schedulePolling()
return () => clearTimeout(timeout)
}, [executeQuery])
(未经测试的代码,但你明白了)
这是在同一主题 (https://github.com/apollographql/apollo-client/issues/6209#issuecomment-676373050) 上提出的建议,所以也许您已经尝试过...