Skip to content

Commit 80fd3a6

Browse files
committed
fix: use ref for ORPCProvider cleanup to avoid stale closure
The useEffect cleanup captured state at mount time ('connecting'), so even after transitioning to 'connected', cleanup never ran. Now stores cleanup function in a ref that's always current.
1 parent b2e87af commit 80fd3a6

File tree

1 file changed

+10
-4
lines changed

1 file changed

+10
-4
lines changed

src/browser/orpc/react.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createContext, useContext, useEffect, useState, useCallback } from "react";
1+
import { createContext, useContext, useEffect, useState, useCallback, useRef } from "react";
22
import { createClient } from "@/common/orpc/client";
33
import { RPCLink as WebSocketLink } from "@orpc/client/websocket";
44
import { RPCLink as MessagePortLink } from "@orpc/client/message-port";
@@ -86,11 +86,15 @@ export const ORPCProvider = (props: ORPCProviderProps) => {
8686
return urlParams.get("token") ?? getStoredAuthToken();
8787
});
8888

89+
// Track cleanup function in a ref to avoid stale closure in useEffect cleanup
90+
const cleanupRef = useRef<(() => void) | null>(null);
91+
8992
const connect = useCallback(
9093
(token: string | null) => {
9194
// If client provided externally, use it directly
9295
if (props.client) {
9396
window.__ORPC_CLIENT__ = props.client;
97+
cleanupRef.current = null; // External client - no cleanup needed
9498
setState({ status: "connected", client: props.client, cleanup: () => undefined });
9599
return;
96100
}
@@ -99,6 +103,7 @@ export const ORPCProvider = (props: ORPCProviderProps) => {
99103
if (window.api) {
100104
const { client, cleanup } = createElectronClient();
101105
window.__ORPC_CLIENT__ = client;
106+
cleanupRef.current = cleanup;
102107
setState({ status: "connected", client, cleanup });
103108
return;
104109
}
@@ -113,6 +118,7 @@ export const ORPCProvider = (props: ORPCProviderProps) => {
113118
.ping("auth-check")
114119
.then(() => {
115120
window.__ORPC_CLIENT__ = client;
121+
cleanupRef.current = cleanup;
116122
setState({ status: "connected", client, cleanup });
117123
})
118124
.catch((err: unknown) => {
@@ -166,9 +172,9 @@ export const ORPCProvider = (props: ORPCProviderProps) => {
166172
connect(authToken);
167173

168174
return () => {
169-
if (state.status === "connected") {
170-
state.cleanup();
171-
}
175+
// Use ref to get current cleanup function - avoids stale closure issue
176+
// where state captured at mount time would always be "connecting"
177+
cleanupRef.current?.();
172178
};
173179
// Only run on mount and when authToken changes via handleAuthSubmit
174180
// eslint-disable-next-line react-hooks/exhaustive-deps

0 commit comments

Comments
 (0)