#reactjs #graphql #react-apollo #react-context
Вопрос:
Для проекта React я использую несколько поставщиков контекста, выполняющих запросы GraphQL, предоставляя все или некоторые компоненты с необходимыми мне данными. Для этого примера у меня есть поставщик книг, который запрашивает все книги для определенного пользователя.
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 amp;amp; 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 amp;amp; 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()
Вопросы
- Почему возникает эта ошибка?
- Как это можно исправить?
- Является ли это хорошей практикой выполнения запросов в контекстных провайдерах?
Ответ №1:
Я попробую еще раз 😉
Вы бы использовали 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) так что, может быть, вы уже пробовали это…
Ответ №2:
Похоже, что ваш крючок запроса graphql опрашивает данные, и это обновит его внутреннее состояние или попытается сделать это, даже если компонент отключен.
Я читал подобные проблемы в этой теме (и это похоже на ошибку, потому 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 amp;amp; children}
</BookContext.Provider>
)
}
Подавляет ли это предупреждение?
Примечание: не уверен, что эта {fetchPolicy: "network-only"}
опция необходима
Комментарии:
1. К сожалению, этого не происходит, я также попробовал это без опроса. Это тоже не нессасери
2. Какую версию клиента apollo вы используете?
3.
^3.3.12
. Все же спасибо за эффективность.