diff --git a/src/rval-react.ts b/src/rval-react.ts index 0fb0f7f..e4de86b 100644 --- a/src/rval-react.ts +++ b/src/rval-react.ts @@ -1,17 +1,19 @@ -import { Observable, sub, isVal, isDrv } from "rval" -import { useState, useEffect } from "react" +import { Observable, sub, isVal, isDrv, effect } from "rval" +import { useState, useEffect, useMemo } from "react" export function useVal(observable: Observable): T { if (!isVal(observable) && !isDrv(observable)) throw new Error("useval - expected val or drv") - const [val, updater] = useState(observable) - useEffect(() => { - const disposer = sub(observable, updater) - // observable has changed before effect was run first time, so trigger additional update - if (observable() !== val) - updater(observable()) - return disposer - }, [observable]) + let f; // forward ref to the updater function, so that we can subscribe first + // this implementation could be simpler if we would first used `useState`, + // and then set up the subscription in useEffect. + // The benefit of this setup, is that we set up the subscription first, + // so that observable is already 'hot' before we read it for the first time + // See: test "useVal - mimimum computations - 2" + const disposer = useMemo(() => sub(observable, x => f(x)), [observable]) + const [val, updater] = useState(observable) // short-cut for initializer with fn: actually: () => observable() + f = updater + useEffect(() => disposer, [observable]) return val } @@ -23,3 +25,22 @@ export function useDrv() { export function render() { } + +export function RValRender({ children }) { + const [tick, setTick] = useState(0) + const { render, dispose } = useMemo(() => { + let render + const dispose = effect( + children, + (didChange, pull) => { + render = pull + if (didChange()) { + setTick(tick + 1) + } + } + ) + return { render, dispose } + }, []) + useEffect(() => dispose, []) + return render() +} diff --git a/tests/react.spec.tsx b/tests/react.spec.tsx index 1caa76d..6ef0d10 100644 --- a/tests/react.spec.tsx +++ b/tests/react.spec.tsx @@ -52,7 +52,7 @@ test('useVal - mimimum computations - 1', async () => { expect(called).toBe(3) }) -test('useVal - mimimum computations - 1', async () => { +test('useVal - mimimum computations - 2', async () => { const counter = val(0) let called = 0 const doubler = drv(() => {