Skip to content
This repository was archived by the owner on Oct 22, 2025. It is now read-only.

Commit 34bab58

Browse files
authored
fix(react): memoize event listener (#1142)
1 parent 91a7caa commit 34bab58

File tree

2 files changed

+36
-22
lines changed

2 files changed

+36
-22
lines changed

packages/frameworks/framework-base/lib/mod.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type {
77
} from "@rivetkit/core/client";
88
import { Derived, Effect, Store, type Updater } from "@tanstack/store";
99

10-
// biome-ignore lint/suspicious/noExplicitAny: its a generic actor registry
1110
export type AnyActorRegistry = Registry<any>;
1211

1312
interface ActorStateReference<AD extends AnyActorDefinition> {
@@ -113,11 +112,27 @@ export interface ActorOptions<
113112
enabled?: boolean;
114113
}
115114

116-
// biome-ignore lint/suspicious/noExplicitAny: actor name can be anything
115+
export type ActorsStateDerived<
116+
Registry extends AnyActorRegistry,
117+
WorkerName extends keyof ExtractActorsFromRegistry<Registry>,
118+
> = Derived<
119+
Omit<
120+
InternalRivetKitStore<
121+
Registry,
122+
ExtractActorsFromRegistry<Registry>
123+
>["actors"][string],
124+
"handle" | "connection"
125+
> & {
126+
handle: ActorHandle<ExtractActorsFromRegistry<Registry>[WorkerName]> | null;
127+
connection: ActorConn<
128+
ExtractActorsFromRegistry<Registry>[WorkerName]
129+
> | null;
130+
}
131+
>;
132+
117133
export type AnyActorOptions = ActorOptions<AnyActorRegistry, any>;
118134

119135
export interface CreateRivetKitOptions<Registry extends AnyActorRegistry> {
120-
// biome-ignore lint/suspicious/noExplicitAny: actor name can be anything
121136
hashFunction?: (opts: ActorOptions<Registry, any>) => string;
122137
}
123138

@@ -144,7 +159,6 @@ export function createRivetKit<
144159
create: () => void;
145160
addEventListener?: (
146161
event: string,
147-
// biome-ignore lint/suspicious/noExplicitAny: need any specific type here
148162
handler: (...args: any[]) => void,
149163
) => void;
150164
}
@@ -158,12 +172,7 @@ export function createRivetKit<
158172
if (cached) {
159173
return {
160174
...cached,
161-
state: cached.state as Derived<
162-
Omit<RivetKitStore["actors"][string], "handle" | "connection"> & {
163-
handle: ActorHandle<Actors[ActorName]> | null;
164-
connection: ActorConn<Actors[ActorName]> | null;
165-
}
166-
>,
175+
state: cached.state as ActorsStateDerived<Registry, ActorName>,
167176
};
168177
}
169178

@@ -328,12 +337,7 @@ export function createRivetKit<
328337
return {
329338
mount,
330339
setState,
331-
state: derived as Derived<
332-
Omit<RivetKitStore["actors"][string], "handle" | "connection"> & {
333-
handle: ActorHandle<Actors[ActorName]> | null;
334-
connection: ActorConn<Actors[ActorName]> | null;
335-
}
336-
>,
340+
state: derived as ActorsStateDerived<Registry, ActorName>,
337341
create,
338342
key,
339343
};

packages/frameworks/react/src/mod.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import {
55
type CreateRivetKitOptions,
66
createRivetKit as createVanillaRivetKit,
77
} from "@rivetkit/framework-base";
8+
import { useEffect, useRef } from "react";
89
import { useStore } from "@tanstack/react-store";
9-
import { useEffect } from "react";
1010

1111
export { createClient } from "@rivetkit/core/client";
1212

@@ -57,19 +57,29 @@ export function createRivetKit<Registry extends AnyActorRegistry>(
5757
* @param eventName The name of the event to listen for.
5858
* @param handler The function to call when the event is emitted.
5959
*/
60-
const useEvent = (
60+
function useEvent(
6161
eventName: string,
6262
// biome-ignore lint/suspicious/noExplicitAny: strong typing of handler is not supported yet
6363
handler: (...args: any[]) => void,
64-
) => {
64+
) {
65+
const ref = useRef(handler);
66+
const actorState = useStore(state) || {};
67+
68+
useEffect(() => {
69+
ref.current = handler;
70+
}, [handler]);
71+
6572
// biome-ignore lint/correctness/useExhaustiveDependencies: it's okay to not include all dependencies here
6673
useEffect(() => {
6774
if (!actorState?.connection) return;
6875

69-
const connection = actorState.connection;
70-
return connection.on(eventName, handler);
71-
}, [actorState.connection, actorState.isConnected, eventName, handler]);
76+
function eventHandler(...args: any[]) {
77+
ref.current(...args);
78+
}
79+
return actorState.connection.on(eventName, eventHandler);
80+
}, [actorState.connection, actorState.isConnected, actorState.hash, eventName]);
7281
};
82+
7383
return {
7484
...actorState,
7585
useEvent,

0 commit comments

Comments
 (0)