Intended outcome:
useSubscription should subscribe only once when run inside <StrictMode>
Actual outcome:
useSubscription subscribes twice to the same subscription. I think that this happens because <StrictMode>
intentionally runs component bodies twice. I suppose that same thing could happen in concurrent mode too.
This results in double network traffic. Also, the second subscription won't get unsubscribed when the useSubscription is unmounted.
This can be inspected on chrome network tab:
in the picture, id 15 and 16 are the single useSubscription and receive the same data. After unmount the id 16 was closed and id 15 was left receiving data(not pictured).
I am not familiar with apollo-client codebase, but I think that this is result of the following line being run in function body instead of inside useEffect:
https://github.com/apollographql/apollo-client/blob/23b17fa7283b21966d1c633c52b848f12b4a211e/src/react/hooks/useSubscription.ts#L44
How to reproduce the issue:
Any subscription should behave the same when run in StrictMode.
const SubComponent = ({vesselId}) => {
const { data } = useSubscription(vesselStatus, {
variables: { vesselId }
});
return null;
}
<StrictMode>
<SubComponent vesselId="245" />
</StrictMode>
Workaround is to not use StrictMode, but I'm afraid that will result in code that breaks when concurrent mode lands in React.
Versions
System:
OS: macOS 10.15.3
Binaries:
Node: 12.16.1 - ~/.nvm/versions/node/v12.16.1/bin/node
Yarn: 1.22.0 - ~/.nvm/versions/node/v12.16.1/bin/yarn
npm: 6.13.4 - ~/.nvm/versions/node/v12.16.1/bin/npm
Browsers:
Chrome: 80.0.3987.132
Firefox: 73.0.1
Safari: 13.0.5
npmPackages:
@apollo/client: ^3.0.0-beta.39 => 3.0.0-beta.39
@apollo/link-ws: ^2.0.0-beta.3 => 2.0.0-beta.3
apollo-server-express: ^2.11.0 => 2.11.0
npmGlobalPackages:
apollo-codegen: 0.20.2
apollo: 2.24.0
If there is interest in fixing this, I can try to make a PR of a failing test case? Not sure it can be done cleanly though.
I'm having the same issue. <StrictMode />
e.g. seems to be enabled for create-react-app
by default and causes the useSubscription
hook to subscribe twice on initial render.
Update: strict mode also causes trouble when setting a pollInterval
in useQuery
which then keeps polling even if the component is unmounted.
Update: strict mode also causes trouble when setting a
pollInterval
inuseQuery
which then keeps polling even if the component is unmounted.
Both of these hooks will then fail under concurrent mode.
Yep, running into this issue as well - all of my useSubscription
's are subscribing twice.
Experiencing the same issue. Any news?
Confirmed
We have the same problem. Any updates?
I have just started playing with Subscriptions. So I have a fresh CRA app here, and it is duplicating subscriptions. When I remove StrictMode, it stops.
Same issue for me
Notice that this is true only if you're in development mode.
Try to run a production build and this effect will go away.
Same thing for me. Wasted a couple of hours. I thought that I was going crazy.
Try to run a production build and this effect will go away.
That might hide the symptoms, but having to run a production build when doing active development would get tedious and annoying real fast. We need a real solution for this issue.
I agree, this is annoying. In my case I listen for events and display an alert for each of them in the UI which turns out to be duplicated. However the problem does not come from Apollo-Client, it is React strict mode behavior.. I don't think we can expect a fix coming from Apollo to remove this behavior since it is external to Apollo-Client.
I can see multiple way to limit the side effect of this behavior :
I agree, this is annoying. In my case I listen for events and display an alert for each of them in the UI which turns out to be duplicated. However the problem does not come from Apollo-Client, it is React strict mode behavior.. I don't think we can expect a fix coming from Apollo to remove this behavior since it is external to Apollo-Client.
There is a reason for strict mode rendering twice. It is to prevent bugs in the coming concurrent mode. So this should be fixed in apollo-client.
The worst thing is that those duplicated observables leak and never get destroyed, so when you do client.resetStore()
all of them get refetched and this most probably will throw errors because the auth token is not set anymore.
I'm surprised that this issue is completely ignored for almost half a year already.
I'm still getting this issue, returning twice when is triggered the subscription
experiencing this now, seeing duplicate subscriptions. First thing I thought of was to use useEffect
, then I found this thread.
im having this issue also
I have the same issue
The same issue
Same issue
same issue here, please help
https://github.com/apollographql/subscriptions-transport-ws/blob/master/src/client.ts
can't wrap my head around why this is happening
but in DEV mode
this.applyMiddlewares (which returns a promise)
seems to be resolving twice for the same subscription
BUT... with a different id
private executeOperation(options: OperationOptions, handler: (error: Error[], result?: any) => void): string {
if (this.client === null) {
this.connect();
}
const opId = this.generateOperationId();
this.operations[opId] = { options: options, handler };
this.applyMiddlewares(options)
.then(processedOptions => {
this.checkOperationOptions(processedOptions, handler);
if (this.operations[opId]) {
this.operations[opId] = { options: processedOptions, handler };
this.sendMessage(opId, MessageTypes.GQL_START, processedOptions);
}
})
.catch(error => {
this.unsubscribe(opId);
handler(this.formatErrors(error));
});
return opId;
}
the odd thing is, even though this condition exists
if (this.operations[opId]) {
this.operations[opId] = { options: processedOptions, handler };
this.sendMessage(opId, MessageTypes.GQL_START, processedOptions);
}
AND opId
is created only once BEFORE calling
this.applyMiddlewares(...)
The second time the promise is resolved
...the same condition evaluates to true
for an id
that is not created and shouldn't exist in this.operations
didn't dig too much through the code, so this... could be totally unrelated to why the subscription is fired twice
but maybe its a good starting point
👍 same issue
I also can confirm the issue. Does someone have an idea of why this is happening? I don't really understand the relationship between <React.StrictMode/>
and the subscriptions...
I also can confirm the issue. Does someone have an idea of why this is happening? I don't really understand the relationship between
<React.StrictMode/>
and the subscriptions...
Because in development StrictMode causes function component bodies to double-execute: https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects
The useSubscription hook is breaking contract with react by starting the subscription in function body.
I also can confirm the issue. Does someone have an idea of why this is happening? I don't really understand the relationship between
<React.StrictMode/>
and the subscriptions...Because in development StrictMode causes function component bodies to double-execute: https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects
The useSubscription hook is breaking contract with react by starting the subscription in function body.
Thank you very much for the precision. I'll try to look on the useSubscription
hook and maybe submit a PR to fix this.
I get multiple as well
Saw the same thing happening earlier removing StrictMode resolved it.
Most helpful comment
There is a reason for strict mode rendering twice. It is to prevent bugs in the coming concurrent mode. So this should be fixed in apollo-client.