conradkoh
conradkohā€¢11mo ago

React useQuery not updating on reconnect

I am using Convex on React Native with expo. The issue I am facing is that when the Convex client connection state comes back to being "ready" - aka connection is re-established, old queries are not updated. Is there a recommendation on how to resolve this? Considering the useQuery hook returns only the results and has to refetch function, I am unsure of how to proceed without reloading the entire screen. Ideally, the queries should refetch events that occurred since the latest successful update.
32 Replies
ballingt
ballingtā€¢11mo ago
Ideally, the queries should refetch events that occurred since the latest successful update.
Are you hoping to receive all intermediate results of a query instead of the most up-to-date result? So on reconnect, you'd get a screen that flashed through all old values until is got to the newest? useQuery doesn't do this, it only displays the most up to date value. If the query result you see it out of date then something isn't working or hooked up right, but not "refetching events that occurred since the latest successful update" is behaving as expected. @conradkoh curious to hear more, I'm not sure I'm understanding the issue
conradkoh
conradkohOPā€¢11mo ago
hey @ballingt! thanks for getting back. so what iā€™m seeing is that on reconnect, the state is stuck at the state before disconnect, and does not update. I may need to do a little more extensive testing
ballingt
ballingtā€¢11mo ago
What does reconnect mean here So when your mobile app is offline and comes back online, and then Convex WebSocket which was constantly trying to reconnect finally reconnects, you don't see the new values? I'm curious about whether the Convex WebSocket connection has actually reconnected. Can you see console.log output? Reconnect messages should be displayed already there, but you can add "{ verbose: true}" to the second argument of the ConvexReactClient constructor to see more.
conradkoh
conradkohOPā€¢11mo ago
reconnect in my context, is based on when the property client.connectionState().isWebSocketConnected goes from false to true. https://github.com/conradkoh/baby-tracker/blob/master/apps/mobile/providers/ConvexClientProvider.tsx#L40-L88 just for context, this is what I am doing. I have a function that polls the client state to check for when it has reconnected. what has worked so far for me is to fully unmount the component that has the useQuery hook call, and remount it, when the connection state has changed. i created a HOC to wrap around any component that may be subscribed to a query. https://github.com/conradkoh/baby-tracker/blob/master/apps/mobile/providers/ConvexClientProvider.tsx#L105-L129 I do hope that it is not some weird react or expo thing that is causing the issue. I'll try to reliably reproduce the issue. I am trying to reproduce the issue. The first thing that seems to be blocking me is that when I turn off my internet on my macbook, and then turn it back on, I still do not get reliable reconnections of the websocket. I tried the safari inside the simulator as well, and it seems to be working though. although this doesn't happen to me in a built version on the app itself.
ballingt
ballingtā€¢11mo ago
reliable reconnections as in timing, sometimes it takes many seconds? Or sometimes it never happens?
conradkoh
conradkohOPā€¢11mo ago
but yeah sorry I think debugging expo etc is probably out of scope. in terms of the convex react library itself, I think it is safe to say 1. there are instances where the isWebsocketConnected is true where it cannot be (aka internet is off) 2. there are instances where it isWebsocketConnected is false (log shows that library also cannot connect back), even though internet is on.
conradkoh
conradkohOPā€¢11mo ago
I think never happens haha. can't say never but I have an instance where the internet is back on for the past 5 mins, but I am getting this (refer to ss)
No description
conradkoh
conradkohOPā€¢11mo ago
this is the verbose log
No description
ballingt
ballingtā€¢11mo ago
There's no conditional behavior on React Native vs the web in the Convex Client, sounds like we're missing some special behavior we ought to be doing for Expo or React Native WebSockets work if there are differences. I agree debugging Expo should be out of scope here but if you could try making an HTTP request at the same time to ensure the the app really is back online here it might be helpful
conradkoh
conradkohOPā€¢11mo ago
I've tried out what you suggested. in the lines where it says "internet connection is online", it is backed by a script that makes a query to a test API that returns with status 200.
conradkoh
conradkohOPā€¢11mo ago
No description
conradkoh
conradkohOPā€¢11mo ago
you can see that the connection does indeed come back online, but the websocket connection continues to fail to connect.
conradkoh
conradkohOPā€¢11mo ago
ok so I think I might have a little more info where something is going wrong - bear with me. Problem 1: When the internet is no longer available, the websocket is not correctly detecting this. You can see this in the screenshot on a fresh instance 1. Internet is online 2. Turn off Wifi 3. Polling to HTTP API says internet is OFF 4. No more regular Ping events from Web Socket Expected: Every 15 seconds server should ping. Otherwise, the web socket manager should close and reconnect. Observed: For some reason, the web socket manager does not close and reconnect, leaving all queries in the previous state. You can see based on the timestamp that the last ping as at 07:31:50 and then there are no more pings. yet, there is no log to say that the client is attempting to close and reconnect. Problem 2: The connection state seems to be out of sync with whether it is actually connected. This was the original issue I was asking about. But it would likely be hard to debug without ensuring that Problem 1 is first solved.
No description
conradkoh
conradkohOPā€¢11mo ago
taken from /node_modules/convex/src/browser/sync/web_socket_manager.ts
No description
conradkoh
conradkohOPā€¢11mo ago
if this line executed, we should see a line in the log.
No description
ballingt
ballingtā€¢11mo ago
Thanks for the investigation @conradkoh! This sounds like something we need to address. Is this with expo or in the simulator? Also did you mention you only see this in development, but not when the app is built? our first step will be getting a repro, hopefully we can get the same thing with your steps here. I wonder if there are timer implementation differences we're dealing with here for browser vs React native
conradkoh
conradkohOPā€¢11mo ago
@ballingt I think we can eliminate expo as the cause - I just tested it with an example in the browser and get a similar behavior.
No description
conradkoh
conradkohOPā€¢11mo ago
it can be reproduced with a minimal ConvexClientProvider
'use client';
import { ReactNode, useEffect } from 'react';
import { ConvexProvider, ConvexReactClient } from 'convex/react';

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!, {
verbose: true,
});

export default function ConvexClientProvider({
children,
}: {
children: ReactNode;
}) {
useInternetTest();
return <ConvexProvider client={convex}>{children}</ConvexProvider>;
}
async function useInternetTest() {
useEffect(() => {
const i = setInterval(async () => {
let isConnected = false;
try {
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
if (res.status === 200) {
isConnected = true;
}
} catch (err) {
//na
}
if (isConnected) {
console.log(new Date().toISOString(), 'internet connection is online.');
} else {
console.log(new Date().toISOString(), 'internet connection is offline');
}
}, 15000);
}, []);
}
'use client';
import { ReactNode, useEffect } from 'react';
import { ConvexProvider, ConvexReactClient } from 'convex/react';

const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!, {
verbose: true,
});

export default function ConvexClientProvider({
children,
}: {
children: ReactNode;
}) {
useInternetTest();
return <ConvexProvider client={convex}>{children}</ConvexProvider>;
}
async function useInternetTest() {
useEffect(() => {
const i = setInterval(async () => {
let isConnected = false;
try {
const res = await fetch('https://jsonplaceholder.typicode.com/todos/1');
if (res.status === 200) {
isConnected = true;
}
} catch (err) {
//na
}
if (isConnected) {
console.log(new Date().toISOString(), 'internet connection is online.');
} else {
console.log(new Date().toISOString(), 'internet connection is offline');
}
}, 15000);
}, []);
}
expected: after 30s, the web socket manager should attempt to close and reconnect (I assume this by reading the default setting from the source code, but I may be wrong) actual: the websocket manager does not do anything
ballingt
ballingtā€¢11mo ago
That's terrific, thanks for looking into this more deeply! not good that everyone is exposed to this bug right now but very good that it's happening in an environment it's easy to write integration tests for
conradkoh
conradkohOPā€¢11mo ago
thanks for all the help. convex is awesome btw šŸ™‚ it's so much fun.
ballingt
ballingtā€¢11mo ago
thank you! We'll let you know when this behavior is fixed, planning on a client release on Tuesday and I'll let you know if it's in that.
conradkoh
conradkohOPā€¢11mo ago
nice, that's amazing. thank you!
conradkoh
conradkohOPā€¢11mo ago
actually I was taking another look at this - most likely it is just that the condition was inverted yea? so it should be
if (this.socket.state === "stopped") return; //ignore stopped state
if (this.socket.state === "stopped") return; //ignore stopped state
instead of
if (this.socket.state !== "stopped") return; //bug: this ignores everything except the stopped state
if (this.socket.state !== "stopped") return; //bug: this ignores everything except the stopped state
No description
conradkoh
conradkohOPā€¢11mo ago
hope this helps save some time
ballingt
ballingtā€¢11mo ago
!!! I need to confirm but tentatively I agree, behavior change introduced a month ago https://github.com/get-convex/convex-js/blame/main/src/browser/sync/web_socket_manager.ts#L270-L273 I won't say who introduced this but their name ends with "om" and starts with "T"
conradkoh
conradkohOPā€¢11mo ago
šŸ˜‚ that's hilarious!!! thanks for sharing the repo link too btw, I was trying to find it via npm and didn't see it there, so I assumed the source wasn't available
ballingt
ballingtā€¢11mo ago
thanks so for digging in, we need a special badge or something for bug finders we're working on our OSS story, atm it's just once-per-release exports to the public repo
ballingt
ballingtā€¢11mo ago
oh thanks for pointing that out, yeah no link on https://www.npmjs.com/package/convex !
npm
convex
Client for the Convex Cloud. Latest version: 1.9.0, last published: 9 days ago. Start using convex in your project by running npm i convex. There is 1 other project in the npm registry using convex.
conradkoh
conradkohOPā€¢11mo ago
that's cool. would love to contribute in some way when that happens! although I'm sure the magic sauce of the backend will remain closed yea? :p
ballingt
ballingtā€¢11mo ago
I just added the link (edit: won't be up until the next release), looking at writing tests for this bug report now No! Well somewhat, but work is well underway to open source the backend too! Plenty of magic sauce in there, although yes also plenty of code around deploying backends at scale that will not be included.
conradkoh
conradkohOPā€¢11mo ago
šŸ¤Æ that's nuts, and so amazing, can't wait! thanks for this, you move super quick!
Luc Ledo
Luc Ledoā€¢11mo ago
Glad to hear, being able to self-host is the biggest determining factor right now for me when comparing to supabase.

Did you find this page helpful?