Hey all! To enable real time messaging for my expo...
# help
s
Hey all! To enable real time messaging for my expo/react native app, I'm using a Supabase subscription. When the app loads, a useEffect gets triggered, and the subscription starts up with status "SUBSCRIBED". Immediately after, however, I'll many times see "CLOSED" and then sometimes "RETRYING_AFTER_TIMEOUT". In Supabase I've enabled realtime on my messages table, and I can see in my dashboard that realtime requests are being made. Here's the code for setting up the subscription & listening to changes to the subscription's status: useEffect(() => { const subscription = supabase .from("messages") .on("INSERT", (payload) => { console.log("Message send received!"); setMessages([...messages, payload.new]); }) .subscribe((status) => console.log("subscribe", status)); return () => { supabase.removeSubscription(subscription); }; }, [messages]); Does anyone know why the subscription is immediately closing - and what I can do to prevent that from happening?
n
Hello @Sobhan! This thread has been automatically created from your message in #843999948717555735 a few seconds ago. We have already mentioned the @User so that they can see your message and help you as soon as possible! Want to unsubscribe from this thread? Right-click the thread in Discord (or use the ``...`` menu) and select "Leave Thread" to unsubscribe from future updates. Want to change the title? Use the ``/title`` command! We have solved your problem? Click the button below to archive it.
g
Probably related to this, https://github.com/supabase/realtime-js/issues/121 But would need to see a trace of your websockets to know if it is that or some other related issue. Realtime requires a 60 heartbeat and when apps go into background that is not guaranteed.
n
Sobhan (2022-06-02)
s
Hey Gary, read through the github thread and not totally sure if it's the same issue I'm having because: My error occurs right after the app loads. The subscription drops right after the react context its stored in mounts - not after 60 seconds.
g
It is not that issue then.
s
Huh, in that case is the problem most likely with my code or Supabase?
Could you advise on any potential next steps?
g
Your call looks fine. I would guess something about your react native setup and realtime. I've not used it. Can you see network requests, in particular the websocket messages?
https://github.com/supabase/realtime/issues/254 just throwing stuff out there at the moment, but that looks close as far as error symptoms, but not sure on environment.
s
Hey Gary, thank you so much for your help! I found a (kind of bad) but workable solution, and learned some stuff about Supabase realtime in the process. Main learning: Supabase doesn't like it when you quickly subscribe, unsusbscribe, and susbscribe again. So if you're doing react development, and your context re-renders multiple times, then Supabase will glitch. My code: I had a 'useEffect' hook that ran when the app loaded. useEffect has a 'dependency array' and whenever an item in that array updates value, the entire context re-renders. Because I was updating the 'messages' state with all the previous messages + the new message from my supabase subscription, each time I updated the 'messages' state, I triggered a re-render of the context, leading to a very quick subscribe -> unsubscribe -> subscribe, causing an error with Supabase. I've included the code that works for me below. Note that I'm purposefully excluding the 'messages' state from the useEffect dependency array to prevent a re-render - but that excluding the 'messages' state means I'm not adhering to best practices.
const [messages, setMessages] = useState([]); const [delayedStart, setDelayedStart] = useState(false); useEffect(() => { setTimeout(() => setDelayedStart(true), 420); }, []); useEffect(() => { if (!delayedStart) return () => null; // Listen for messages const subscription = supabase .from("messages") .on("INSERT", (payload) => { console.log("Message send received!"); setMessages([...messages, payload.new]); }) .subscribe((status) => console.log("subscribe", status)); return () => { supabase.removeSubscription(subscription); }; }, [delayedStart]);
g
@Sobhan I'm sure the key was the 420... 😎
s
Yes hahaha - I should probably experiment with different delay amounts lmao
Update! Below is better code - that doesn't break react's rules:
const [delayedStart, setDelayedStart] = useState(false); useEffect(() => { setTimeout(() => setDelayedStart(true), 420); }, []); useEffect(() => { if (!delayedStart) return () => null; // Listen for messages const subscription = supabase .from("messages") .on("INSERT", (payload) => { console.log("Message send received!"); if (payload.new.organization_id === userProfile.active_org_id) { console.log( "Value of old messages:", messages, "Value of new message:", payload.new ); setMessages([...messages, payload.new]); setDelayedStart(false); setTimeout(() => setDelayedStart(true), 420); } }) .subscribe((status) => console.log("subscribe", status)); return () => { // setDelayedStart(false); supabase.removeSubscription(subscription); }; }, [delayedStart, userProfile, messages]);