AlwayzPatrick
AlwayzPatrick10mo ago

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:
Found my issue! The solution it to change App.tsx ```tsx import routes from "./pages/_routes"; import { RouterProvider } from "react-router-dom"...
Jump to solution
6 Replies
AlwayzPatrick
AlwayzPatrick10mo ago
App.tsx
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;
Home.tsx
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>
);
}
Changing the link to a split link and checking on the operation "subscription" causes the error
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
AlwayzPatrick
AlwayzPatrick10mo ago
Found my issue! The solution it to change App.tsx
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;
So it has a split link and only split when the operation is a subscription.
AlwayzPatrick
AlwayzPatrick10mo ago
In the actual file you need to change the code to access the observer.
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>
);
}
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 :)
AlwayzPatrick
AlwayzPatrick10mo ago
Possibly adding some examples on how to use wsLink in React for the docs would be handy :)
kamama
kamama10mo ago
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
AlwayzPatrick
AlwayzPatrick10mo ago
Yep hence I use the React component trpc.Provider It should reload the connection each time on mount
More Posts