From 87e7260c1613daee06f56c5f2e6ef44957abe04e Mon Sep 17 00:00:00 2001 From: littensy <56808540+littensy@users.noreply.github.com> Date: Thu, 2 Oct 2025 19:30:40 -0700 Subject: [PATCH] fix: wrap hooks in untracked, update types --- packages/charm-sync/src/index.d.ts | 6 +-- packages/charm-sync/src/server.luau | 2 +- packages/react-charm/src/init.luau | 4 +- .../src/__tests__/useAtom.spec.luau | 42 +++++++++++++++---- packages/vide-charm/src/init.luau | 2 +- 5 files changed, 40 insertions(+), 16 deletions(-) diff --git a/packages/charm-sync/src/index.d.ts b/packages/charm-sync/src/index.d.ts index ca736db..edbfc39 100644 --- a/packages/charm-sync/src/index.d.ts +++ b/packages/charm-sync/src/index.d.ts @@ -115,7 +115,7 @@ export namespace client { export function reset(): void; - export function applyPayloads }, Serialize extends boolean>( + export function applyPayloads }, Serialize extends boolean = true>( payloads: SyncPayload[], ): void; } @@ -127,8 +127,8 @@ export namespace server { export function unsyncClient(client: Player): void; - export function startSync }, Serialize extends boolean>( - onSync: (client: Player, payload: SyncPayload) => void, + export function startSync }, Serialize extends boolean = true>( + onSync: (client: Player, payloads: SyncPayload[]) => void, ): Cleanup; export function stopSync(): void; diff --git a/packages/charm-sync/src/server.luau b/packages/charm-sync/src/server.luau index 2d3789d..d9009b1 100644 --- a/packages/charm-sync/src/server.luau +++ b/packages/charm-sync/src/server.luau @@ -301,7 +301,7 @@ local function startSync(onSync: (client: any, payloads: { SyncPayload }) -> ()) timer += deltaTime if timer >= config.interval then - timer %= config.interval + timer = 0 debug.profilebegin("CharmSync") flush() debug.profileend() diff --git a/packages/react-charm/src/init.luau b/packages/react-charm/src/init.luau index c81d7dd..44f43f9 100644 --- a/packages/react-charm/src/init.luau +++ b/packages/react-charm/src/init.luau @@ -16,7 +16,7 @@ local function useAtom(getter: () -> T, dependencies: { any }?): T React.useLayoutEffect(function() return Charm.effect(function() - setState(getter()) + Charm.untracked(setState, getter()) end) end, dependencies or {}) @@ -39,7 +39,7 @@ local function useAtomBinding(getter: () -> T, dependencies: { any }?) React.useLayoutEffect(function() return Charm.effect(function() - setBinding(getter()) + Charm.untracked(setBinding, getter()) end) end, dependencies or {}) diff --git a/packages/vide-charm/src/__tests__/useAtom.spec.luau b/packages/vide-charm/src/__tests__/useAtom.spec.luau index 5f4a801..88c6b5b 100644 --- a/packages/vide-charm/src/__tests__/useAtom.spec.luau +++ b/packages/vide-charm/src/__tests__/useAtom.spec.luau @@ -3,16 +3,13 @@ local Vide = require("../../Vide") local VideCharm = require("../") local suite = require("../../../../test/suite") -local atom = Charm.atom -local root = Vide.root -local useAtom = VideCharm.useAtom local test = suite.test test("tracks atom state", function() - local state = atom(0) + local state = Charm.atom(0) local source - local cleanup = root(function() - source = useAtom(state) + local cleanup = Vide.root(function() + source = VideCharm.useAtom(state) end) assert(source() == 0, "should return 0") state(1) @@ -21,10 +18,10 @@ test("tracks atom state", function() end) test("accepts a selector", function() - local state = atom(0) + local state = Charm.atom(0) local source - local cleanup = root(function() - source = useAtom(function() + local cleanup = Vide.root(function() + source = VideCharm.useAtom(function() return 2 * state() end) end) @@ -34,4 +31,31 @@ test("accepts a selector", function() cleanup() end) +test("updates source in untracked", function() + local outerAtom = Charm.atom(nil :: any) + local innerAtom = Charm.atom(0) + local spyCalls = 0 + local cleanup = Vide.root(function() + return Vide.show(VideCharm.useAtom(outerAtom), function() + -- Charm effects created in Vide scopes triggered by useAtom might + -- incorrectly get treated as an inner effect in useAtom. This test + -- ensures that doesn't happen. + Vide.cleanup(Charm.effect(function() + innerAtom() + spyCalls += 1 + end)) + return nil + end) + end) + assert(spyCalls == 0, "should not call spy initially") + outerAtom(true) + assert(spyCalls == 1, "should call spy when visible") + innerAtom(1) + assert(spyCalls == 2, "spy should run when counter updates") + outerAtom({}) -- set to a truthy value to trigger an outer effect re-run + innerAtom(2) + assert(spyCalls == 3, "spy should still run when counter updates") + cleanup() +end) + return {} diff --git a/packages/vide-charm/src/init.luau b/packages/vide-charm/src/init.luau index b2d27d8..bf59c2e 100644 --- a/packages/vide-charm/src/init.luau +++ b/packages/vide-charm/src/init.luau @@ -10,7 +10,7 @@ local function useAtom(getter: () -> State): () -> State local state = Vide.source(getter()) Vide.cleanup(Charm.effect(function() - state(getter()) + Charm.untracked(state :: (State) -> State, getter()) end)) return state