Intended outcome:
When the component containing my ApolloProvider re-renders, the children only re-render if the ApolloClient instance has changed (by reference equality).
Actual outcome:
Every time the provider is re-rendered, the children re-render too, even if the ApolloClient instance is referentially identical.
How to reproduce the issue:
I think this is because the context value has structure {client: ApolloClient}, the provider creates a new object on every render – see https://github.com/apollographql/react-apollo/blob/4163f28531c70955ca7ad3576330e4b394d04b51/packages/common/src/context/ApolloProvider.tsx#L21
It should be a reasonably straightforward PR to construct this dictionary using useMemo instead, ensuring that the Provider can re-render without every Consumer/useApollo re-rendering unnecessarily.
Version
System:
OS: macOS Mojave 10.14.6
Binaries:
Node: 11.4.0 - ~/.nvm/versions/node/v11.4.0/bin/node
Yarn: 1.12.3 - ~/workspace/react-native/node_modules/.bin/yarn
npm: 6.4.1 - ~/.nvm/versions/node/v11.4.0/bin/npm
Browsers:
Chrome: 77.0.3865.120
Firefox: 68.0.2
Safari: 13.0.2
npmPackages:
@apollo/react-components: ^3.1.1 => 3.1.1
@apollo/react-hooks: ^3.1.1 => 3.1.1
apollo: ^2.1.8 => 2.1.8
apollo-cache-inmemory: ^1.6.3 => 1.6.3
apollo-cache-persist: ^0.1.1 => 0.1.1
apollo-client: ^2.6.4 => 2.6.4
apollo-link-batch-http: ^1.2.13 => 1.2.13
apollo-link-context: ^1.0.19 => 1.0.19
apollo-link-http: ^1.5.16 => 1.5.16
You just saved my day... I'm having the same issue and I never thought this could be the cause
I've also encountered this issue with @apollo/[email protected]. Before this is fixed on the main branch, I wonder whether it's possible to wrap ApolloProvider into another context provider that memoizes the client?
Any updates on this? I installed 3.2.0-beta.0 but the issue seems to still be there. There is a noticeable lag in my app because of this re-render.
Can confirm, having this issue with @apollo/[email protected]. When using the useToasts() hook to toast when an error occured on a query, the component keeps rerendering in an infinite loop.
can also confirm this
System:
OS: macOS 10.15.2
Binaries:
Node: 12.13.1 - ~/.nvm/versions/node/v12.13.1/bin/node
Yarn: 1.19.1 - /usr/local/bin/yarn
npm: 6.12.1 - ~/.nvm/versions/node/v12.13.1/bin/npm
Browsers:
Chrome: 79.0.3945.117
Firefox: 71.0
Safari: 13.0.4
npmPackages:
@apollo/react-hooks: ^3.0.0 => 3.1.3
@apollo/react-ssr: ^3.0.0 => 3.1.3
apollo-boost: ^0.4.3 => 0.4.4
apollo-cache-inmemory: ^1.6.2 => 1.6.3
apollo-link-batch-http: ^1.2.13 => 1.2.13
apollo-link-context: ^1.0.17 => 1.0.19
apollo-link-error: ^1.1.12 => 1.1.12
apollo-link-persisted-queries: ^0.2.2 => 0.2.2
apollo-link-schema: ^1.2.2 => 1.2.4
did anyone found a workaround?
I've also encountered this issue with
@apollo/[email protected]. Before this is fixed on the main branch, I wonder whether it's possible to wrapApolloProviderinto another context provider that memoizes theclient?
i think the client is usually referential equal, so memoizing it does not make a difference. I think its more of a problem of ApolloProvider itself. e.g. when its children change or its parent rerenders. This should definitly not lead to all useQuery components to rerender
I think this is because the context value has structure
{client: ApolloClient}, the provider creates a new object on every render – see
think this is because the context value has structure
{client: ApolloClient}, the provider creates a new object on every render – seeIt should be a reasonably straightforward PR to construct this dictionary using
useMemoinstead, ensuring that the Provider can re-render without every Consumer/useApollo re-rendering unnecessarily.
i think that is not the problem, as there is a check that if the client is still the same, it won't create a new object. It might have more to do with the consumers? (just a guess)
I fixed it for my own purpose by writing my own ApolloProvider:
import React, { useMemo } from "react";
import { getApolloContext } from "@apollo/react-common";
export default function MyApolloProvider({ client, children }) {
const ApolloContext = getApolloContext();
const value = useMemo(() => ({ client }), [client]);
return (
);
}
I fixed it for my own purpose by writing my own ApolloProvider:
import React, { useMemo } from "react";
import { getApolloContext } from "@apollo/react-common";
export default function MyApolloProvider({ client, children }) {
const ApolloContext = getApolloContext();
const value = useMemo(() => ({ client }), [client]);
return (
);
}
yeah! this works!
Edit: it seems to break server side rendering. @robertsmit did you experience something similar?
@hwillson do you know if this is fixed in the upcoming version 4? I think this can be a performance killer in certain applications (e.g. in nextjs)
@macrozone I only use that code for client side rendering. So probably it only works for that purpose and not server side.
@macrozone I only use that code for client side rendering. So probably it only works for that purpose and not server side.
i did a simple workaround:
// see https://github.com/apollographql/react-apollo/issues/3595#issuecomment-577586255
import { ApolloProvider, getApolloContext } from "@apollo/react-common";
import { useMemo } from "react";
const CustomApolloProvider = ({ client, children }) => {
const ApolloContext = getApolloContext();
const value = useMemo(() => ({ client }), [client]);
return <ApolloContext.Provider value={value}>{children}</ApolloContext.Provider>;
};
// use normal for ssr
export default process.browser ? CustomApolloProvider : ApolloProvider;
Any Updates?
Most helpful comment
i did a simple workaround: