Skip to content

Commit 18f1aa1

Browse files
authored
Reflect logic from #754 in react (#757)
* Reflect logic from #754 in react * Update little-shoes-talk.md with pull request logic
1 parent 5db1295 commit 18f1aa1

File tree

3 files changed

+68
-3
lines changed

3 files changed

+68
-3
lines changed

.changeset/little-shoes-talk.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@preact/signals-react": patch
3+
---
4+
5+
Reflect logic from https://github.com/preactjs/signals/pull/754 in react

packages/react/runtime/src/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -399,9 +399,13 @@ export function useComputed<T>(
399399
compute: () => T,
400400
options?: SignalOptions<T>
401401
): ReadonlySignal<T> {
402-
const $compute = useRef(compute);
403-
$compute.current = compute;
404-
return useMemo(() => computed<T>(() => $compute.current(), options), Empty);
402+
const [$fn, $computed] = useMemo(() => {
403+
const $fn = signal(compute);
404+
return [$fn, computed(() => $fn.value(), options)] as const;
405+
}, []);
406+
407+
$fn.value = compute;
408+
return $computed;
405409
}
406410

407411
export function useSignalEffect(

packages/react/test/shared/updates.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
useContext,
1919
createContext,
2020
useRef,
21+
useCallback,
2122
} from "react";
2223
import type { FunctionComponent } from "react";
2324
import { renderToStaticMarkup } from "react-dom/server";
@@ -848,4 +849,59 @@ export function updateSignalsTests(usingTransform = false) {
848849
expect(cleanup).to.have.been.calledWith("foo", isReact16 ? child : null);
849850
});
850851
});
852+
853+
describe("useComputed", () => {
854+
it("should recompute and update dependency list when the compute function changes", async () => {
855+
const s1 = signal(1);
856+
const s2 = signal("a");
857+
858+
function App({ x }: { x: Signal }) {
859+
const fn = useCallback(() => {
860+
return x.value;
861+
}, [x]);
862+
863+
const c = useComputed(fn);
864+
return <span>{c.value}</span>;
865+
}
866+
867+
await render(<App x={s1} />);
868+
expect(scratch.textContent).to.equal("1");
869+
870+
await render(<App x={s2} />);
871+
expect(scratch.textContent).to.equal("a");
872+
873+
await act(() => {
874+
s1.value = 2;
875+
});
876+
expect(scratch.textContent).to.equal("a");
877+
878+
await act(() => {
879+
s2.value = "b";
880+
});
881+
expect(scratch.textContent).to.equal("b");
882+
});
883+
884+
it("should not recompute when the compute function doesn't change and dependency values don't change", async () => {
885+
const s1 = signal(1);
886+
const spy = sinon.spy();
887+
888+
function App({ x }: { x: Signal }) {
889+
const fn = useCallback(() => {
890+
spy();
891+
return x.value;
892+
}, [x]);
893+
894+
const c = useComputed(fn);
895+
return <span>{c.value}</span>;
896+
}
897+
898+
await render(<App x={s1} />);
899+
expect(scratch.textContent).to.equal("1");
900+
expect(spy).to.have.been.calledOnce;
901+
902+
await render(<App x={s1} />);
903+
expect(scratch.textContent).to.equal("1");
904+
expect(spy).to.have.been.calledOnce;
905+
});
906+
});
851907
}

0 commit comments

Comments
 (0)