Implementing wsLink causes issues on React
I get an error within the console stating
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onError')
at Object.onError (createHooksInternal-f1d4019d.mjs:454:41)
at Object.error (index.mjs:80:17)
at Object.error (index.mjs:20:25)
at Object.error (observable-ade1bad8.mjs:55:21)
at Object.error (observable-ade1bad8.mjs:55:21)
at httpBatchLink-cee1f56c.mjs:200:34
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onError')
at Object.onError (createHooksInternal-f1d4019d.mjs:454:41)
at Object.error (index.mjs:80:17)
at Object.error (index.mjs:20:25)
at Object.error (observable-ade1bad8.mjs:55:21)
at Object.error (observable-ade1bad8.mjs:55:21)
at httpBatchLink-cee1f56c.mjs:200:34
Solution:Jump to solution
Found my issue! The solution it to change
App.tsx
```tsx
import routes from "./pages/_routes";
import { RouterProvider } from "react-router-dom"...
6 Replies
App.tsx
Home.tsx
Changing the link to a split link and checking on the operation "subscription" causes the error
import routes from "./pages/_routes";
import { RouterProvider } from "react-router-dom"
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createWSClient, httpBatchLink, wsLink } from '@trpc/client';
import { useState } from 'react';
import { trpc } from '@/util/_trpc';
import Cookies from "js-cookie";
function App() {
const headers: any = {};
if (Cookies.get("token")) {
headers["authorization"] = `Bearer ${Cookies.get("token")}`;
}
const ws = createWSClient({
url: import.meta.env.VITE_WS_TRPC ?? "",
})
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: import.meta.env.VITE_API_TRPC ?? "",
// You can pass any HTTP headers you wish here
async headers() {
return {
...headers
};
},
}),
wsLink({
client: ws
})
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={routes} fallbackElement={<h1>Loading...</h1>} />
</QueryClientProvider>
</trpc.Provider>
);
}
export default App;
import routes from "./pages/_routes";
import { RouterProvider } from "react-router-dom"
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createWSClient, httpBatchLink, wsLink } from '@trpc/client';
import { useState } from 'react';
import { trpc } from '@/util/_trpc';
import Cookies from "js-cookie";
function App() {
const headers: any = {};
if (Cookies.get("token")) {
headers["authorization"] = `Bearer ${Cookies.get("token")}`;
}
const ws = createWSClient({
url: import.meta.env.VITE_WS_TRPC ?? "",
})
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: import.meta.env.VITE_API_TRPC ?? "",
// You can pass any HTTP headers you wish here
async headers() {
return {
...headers
};
},
}),
wsLink({
client: ws
})
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={routes} fallbackElement={<h1>Loading...</h1>} />
</QueryClientProvider>
</trpc.Provider>
);
}
export default App;
import { trpc } from "@/util/_trpc";
export default function Home() {
const healthyQuery = trpc.healthy.useQuery();
const onPermission = trpc.users.user.permissions.onGive.useSubscription();
return (
<div>
<h1>Home</h1>
<p>Server is {healthyQuery.data === "OK" ? "Healthy" : "Dead"}</p>
<p>This is the home page.</p>
</div>
);
}
import { trpc } from "@/util/_trpc";
export default function Home() {
const healthyQuery = trpc.healthy.useQuery();
const onPermission = trpc.users.user.permissions.onGive.useSubscription();
return (
<div>
<h1>Home</h1>
<p>Server is {healthyQuery.data === "OK" ? "Healthy" : "Dead"}</p>
<p>This is the home page.</p>
</div>
);
}
uncaught TypeError: Cannot read properties of undefined (reading 'onStarted')
at Object.onStarted (createHooksInternal-f1d4019d.mjs:443:41)
at Object.next (index.mjs:72:21)
at Object.next (index.mjs:15:25)
at Object.next (observable-ade1bad8.mjs:48:21)
at Object.next (observable-ade1bad8.mjs:48:21)
at Object.next (observable-ade1bad8.mjs:48:21)
at Object.next (observable-ade1bad8.mjs:48:21)
at Object.next (wsLink.mjs:259:34)
at handleIncomingResponse (wsLink.mjs:119:13)
at WebSocket.<anonymous> (wsLink.mjs:135:17)
uncaught TypeError: Cannot read properties of undefined (reading 'onStarted')
at Object.onStarted (createHooksInternal-f1d4019d.mjs:443:41)
at Object.next (index.mjs:72:21)
at Object.next (index.mjs:15:25)
at Object.next (observable-ade1bad8.mjs:48:21)
at Object.next (observable-ade1bad8.mjs:48:21)
at Object.next (observable-ade1bad8.mjs:48:21)
at Object.next (observable-ade1bad8.mjs:48:21)
at Object.next (wsLink.mjs:259:34)
at handleIncomingResponse (wsLink.mjs:119:13)
at WebSocket.<anonymous> (wsLink.mjs:135:17)
Solution
Found my issue! The solution it to change
App.tsx
So it has a split link and only split when the operation is a subscription.
import routes from "./pages/_routes";
import { RouterProvider } from "react-router-dom"
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createWSClient, httpBatchLink, wsLink, splitLink } from '@trpc/client';
import { useState } from 'react';
import { trpc } from '@/util/_trpc';
import Cookies from "js-cookie";
function App() {
const headers: any = {};
if (Cookies.get("token")) {
headers["authorization"] = `Bearer ${Cookies.get("token")}`;
}
const ws = createWSClient({
url: import.meta.env.VITE_WS_TRPC ?? "",
})
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
splitLink({
condition(op) {
return op.type === "subscription";
},
true: wsLink({
client: ws
}),
false: httpBatchLink({
url: import.meta.env.VITE_API_TRPC ?? "",
// You can pass any HTTP headers you wish here
async headers() {
return {
...headers
};
},
}),
}),
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={routes} fallbackElement={<h1>Loading...</h1>} />
</QueryClientProvider>
</trpc.Provider>
);
}
export default App;
import routes from "./pages/_routes";
import { RouterProvider } from "react-router-dom"
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createWSClient, httpBatchLink, wsLink, splitLink } from '@trpc/client';
import { useState } from 'react';
import { trpc } from '@/util/_trpc';
import Cookies from "js-cookie";
function App() {
const headers: any = {};
if (Cookies.get("token")) {
headers["authorization"] = `Bearer ${Cookies.get("token")}`;
}
const ws = createWSClient({
url: import.meta.env.VITE_WS_TRPC ?? "",
})
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
splitLink({
condition(op) {
return op.type === "subscription";
},
true: wsLink({
client: ws
}),
false: httpBatchLink({
url: import.meta.env.VITE_API_TRPC ?? "",
// You can pass any HTTP headers you wish here
async headers() {
return {
...headers
};
},
}),
}),
],
}),
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={routes} fallbackElement={<h1>Loading...</h1>} />
</QueryClientProvider>
</trpc.Provider>
);
}
export default App;
In the actual file you need to change the code to access the observer.
I could've known this since its the same syntax for a normal client which I did implement but I guess I just was tired :)
import { trpc } from "@/util/_trpc";
export default function Home() {
const healthyQuery = trpc.healthy.useQuery();
trpc.users.user.permissions.onGive.useSubscription(undefined, {
onStarted() {
console.log("Started");
},
onData(data) {
console.log(data);
}
});
return (
<div>
<h1>Home</h1>
<p>Server is {healthyQuery.data === "OK" ? "Healthy" : "Dead"}</p>
<p>This is the home page.</p>
</div>
);
}
import { trpc } from "@/util/_trpc";
export default function Home() {
const healthyQuery = trpc.healthy.useQuery();
trpc.users.user.permissions.onGive.useSubscription(undefined, {
onStarted() {
console.log("Started");
},
onData(data) {
console.log(data);
}
});
return (
<div>
<h1>Home</h1>
<p>Server is {healthyQuery.data === "OK" ? "Healthy" : "Dead"}</p>
<p>This is the home page.</p>
</div>
);
}
Possibly adding some examples on how to use wsLink in React for the docs would be handy :)
here is a problem, if the user haven't signin. The websocket connection is created using the cookie without session token. Once you signed in, the websocket connection is still without session token, we have to recreate the connect .
only two way to solve the issue, 1: wslink expose reconnect method, 2: create the client in React component
Yep hence I use the React component
trpc.Provider
It should reload the connection each time on mount