From 86b50222e261253cce82e018832d91b5efca79a9 Mon Sep 17 00:00:00 2001 From: Sunil Pai Date: Sun, 22 Feb 2026 18:56:26 +0000 Subject: [PATCH] Declare optional React peerDependency Add an explicit, optional React peerDependency for partysocket so strict package managers (e.g. pnpm) correctly resolve react for the partysocket/react and partysocket/use-ws subpath exports. This includes a new changeset and package.json updates. Also: tighten test typings (import and use WsWebSocket instead of any), rename an unused handler param to _lobby to avoid linter noise, and add explanatory comments plus a biome-ignore on the memoized socket options to avoid unnecessary reconnects when callers pass inline option objects. --- .changeset/react-peer-dep.md | 5 +++++ fixtures/hono/src/server.ts | 2 +- package-lock.json | 8 ++++++++ packages/partysocket/package.json | 8 ++++++++ packages/partysocket/src/tests/react-hooks.test.tsx | 8 ++++---- packages/partysocket/src/use-socket.ts | 6 +++++- 6 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 .changeset/react-peer-dep.md diff --git a/.changeset/react-peer-dep.md b/.changeset/react-peer-dep.md new file mode 100644 index 00000000..fae9a7aa --- /dev/null +++ b/.changeset/react-peer-dep.md @@ -0,0 +1,5 @@ +--- +"partysocket": patch +--- + +Declare `react` as an optional peer dependency so strict package managers like pnpm correctly resolve it for the `partysocket/react` and `partysocket/use-ws` subpath exports. diff --git a/fixtures/hono/src/server.ts b/fixtures/hono/src/server.ts index 733fdd63..b8a51344 100644 --- a/fixtures/hono/src/server.ts +++ b/fixtures/hono/src/server.ts @@ -21,7 +21,7 @@ app.use( "*", partyserverMiddleware<{ Bindings: Bindings }>({ options: { - onBeforeConnect(req, lobby, c) { + onBeforeConnect(req, _lobby, c) { const url = new URL(req.url); const token = url.searchParams.get("token"); if (!token) { diff --git a/package-lock.json b/package-lock.json index 6ed4a219..ecbfcb75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12320,6 +12320,14 @@ "devDependencies": { "@testing-library/react": "^16.3.1", "@types/ws": "^8.18.1" + }, + "peerDependencies": { + "react": ">=17" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } } }, "packages/partysub": { diff --git a/packages/partysocket/package.json b/packages/partysocket/package.json index 182d5719..be40f6ee 100644 --- a/packages/partysocket/package.json +++ b/packages/partysocket/package.json @@ -87,6 +87,14 @@ "dependencies": { "event-target-polyfill": "^0.0.4" }, + "peerDependencies": { + "react": ">=17" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + }, "devDependencies": { "@testing-library/react": "^16.3.1", "@types/ws": "^8.18.1" diff --git a/packages/partysocket/src/tests/react-hooks.test.tsx b/packages/partysocket/src/tests/react-hooks.test.tsx index 19ec1156..69f9db5c 100644 --- a/packages/partysocket/src/tests/react-hooks.test.tsx +++ b/packages/partysocket/src/tests/react-hooks.test.tsx @@ -5,7 +5,7 @@ import { renderHook, waitFor } from "@testing-library/react"; import React from "react"; import { afterAll, beforeAll, describe, expect, test, vitest } from "vitest"; -import { WebSocketServer } from "ws"; +import { type WebSocket as WsWebSocket, WebSocketServer } from "ws"; import usePartySocket, { useWebSocket } from "../react"; @@ -320,7 +320,7 @@ describe.skipIf(!!process.env.GITHUB_ACTIONS)("usePartySocket", () => { const onOpen = vitest.fn(); const connectionPromise = new Promise((resolve) => { - wss.once("connection", (_ws: any) => { + wss.once("connection", (_ws: WsWebSocket) => { resolve(); }); }); @@ -355,7 +355,7 @@ describe.skipIf(!!process.env.GITHUB_ACTIONS)("usePartySocket", () => { const onMessage = vitest.fn(); const testMessage = "hello from server"; - const connectionHandler = (ws: any) => { + const connectionHandler = (ws: WsWebSocket) => { setTimeout(() => { ws.send(testMessage); }, 200); @@ -453,7 +453,7 @@ describe.skipIf(!!process.env.GITHUB_ACTIONS)("usePartySocket", () => { const onMessage1 = vitest.fn(); const onMessage2 = vitest.fn(); - const connectionHandler = (ws: any) => { + const connectionHandler = (ws: WsWebSocket) => { setTimeout(() => ws.send("message1"), 100); setTimeout(() => ws.send("message2"), 200); }; diff --git a/packages/partysocket/src/use-socket.ts b/packages/partysocket/src/use-socket.ts index 1089363f..00e3a6f0 100644 --- a/packages/partysocket/src/use-socket.ts +++ b/packages/partysocket/src/use-socket.ts @@ -43,8 +43,12 @@ export function useStableSocket< // extract enabled with default value of true const { enabled = true } = options; - // ensure we only reconnect when necessary + // Returns a stable reference to options, only updating when the serialized + // key changes. This avoids reconnecting on every render when callers pass + // an inline options object (new reference each time) whose values haven't + // actually changed. const shouldReconnect = createOptionsMemoKey(options); + // biome-ignore lint/correctness/useExhaustiveDependencies: shouldReconnect is a serialized key derived from options — we intentionally memo on the key, not the object reference const socketOptions = useMemo(() => { return options; }, [shouldReconnect]);