diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 83b597683..230a60214 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -91,11 +91,17 @@ Object.defineProperties(Signal.prototype, { }); // Track the current owner (roughly equiv to current vnode) -// let currentOwner: ReactOwner; -// Object.defineProperty(internals.ReactCurrentOwner, "current", { -// get() { return currentOwner; }, -// set(owner) { currentOwner = owner; }, -// }); +let lastOwner: ReactOwner | undefined; +let currentOwner: ReactOwner | null = null; +Object.defineProperty(internals.ReactCurrentOwner, "current", { + get() { + return currentOwner; + }, + set(owner) { + currentOwner = owner; + if (currentOwner) lastOwner = currentOwner; + }, +}); // Track the current dispatcher (roughly equiv to current component impl) let lock = false; @@ -108,17 +114,19 @@ Object.defineProperty(internals.ReactCurrentDispatcher, "current", { set(api) { currentDispatcher = api; if (lock) return; - if (api && !isInvalidHookAccessor(api)) { + if (lastOwner && api && !isInvalidHookAccessor(api)) { // prevent re-injecting useReducer when the Dispatcher // context changes to run the reducer callback: lock = true; const rerender = api.useReducer(UPDATE, {})[1]; lock = false; - const currentOwner = internals.ReactCurrentOwner.current; - let updater = updaterForComponent.get(currentOwner); + + let updater = updaterForComponent.get(lastOwner); if (!updater) { updater = createUpdater(rerender); - updaterForComponent.set(currentOwner, updater); + updaterForComponent.set(lastOwner, updater); + } else { + updater._updater = rerender; } setCurrentUpdater(updater); } else { diff --git a/packages/react/test/index.test.tsx b/packages/react/test/index.test.tsx index 45f1df846..94d6ed27b 100644 --- a/packages/react/test/index.test.tsx +++ b/packages/react/test/index.test.tsx @@ -2,7 +2,7 @@ globalThis.IS_REACT_ACT_ENVIRONMENT = true; import { signal, useComputed } from "@preact/signals-react"; -import { createElement, useMemo } from "react"; +import { createElement, useMemo, memo, StrictMode } from "react"; import { createRoot, Root } from "react-dom/client"; import { act } from "react-dom/test-utils"; @@ -158,5 +158,26 @@ describe("@preact/signals-react", () => { }); expect(scratch.textContent).to.equal("bar"); }); + + it("should consistently rerender in strict mode", async () => { + const sig = signal(null!); + + const Test = memo(() =>

{sig.value}

); + const App = () => ( + + + + ); + + for (let i = 0; i < 3; i++) { + const value = `${i}`; + + act(() => { + sig.value = value; + render(); + }); + expect(scratch.textContent).to.equal(value); + } + }); }); });