diff --git a/CHANGELOG.md b/CHANGELOG.md index dfd3354..781312e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Change Log ## [Unreleased] +### Changed +- fix: support changing path #1 ## [0.0.1] - 2023-01-23 ### Added diff --git a/src/index.ts b/src/index.ts index 260368d..82043b4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import { useEffect, useReducer } from 'react'; -import type { ReducerWithoutAction } from 'react'; +import type { DispatchWithoutAction, Reducer } from 'react'; import { subscribe, snapshot } from 'valtio/vanilla'; type AsRef = { $$valtioRef: true }; @@ -31,6 +31,9 @@ const get = ( return result; }; +// HACK the second parameter for snapshot() to path through promise +const handlePromise = (x: Promise) => x as unknown as T; + export function useValtio(proxy: State): Snapshot; export function useValtio< @@ -42,36 +45,39 @@ export function useValtio< State extends object, Path extends readonly unknown[], >(proxy: State, path: Path = [] as any) { - const getSlice = () => - get( - // HACK the second parameter is to avoid handling promise - snapshot(proxy, (x) => x as any), - path, - ); - type Result = ReturnType; + const slice = get(snapshot(proxy, handlePromise), path); const [[sliceFromReducer, proxyFromReducer], rerender] = useReducer< - ReducerWithoutAction, + Reducer, undefined >( - (prev) => { - const nextSlice = getSlice(); + (prev, fromSelf?: boolean) => { + if (fromSelf) { + return [slice, proxy]; + } + const nextSlice = get(snapshot(proxy, handlePromise), path); if (Object.is(prev[0], nextSlice) && prev[1] === proxy) { return prev; } return [nextSlice, proxy]; }, undefined, - () => [getSlice(), proxy], + () => [slice, proxy], ); useEffect(() => { - const unsubscribe = subscribe(proxy, rerender, true); - rerender(); + const unsubscribe = subscribe( + proxy, + rerender as DispatchWithoutAction, + true, + ); + (rerender as DispatchWithoutAction)(); return unsubscribe; }, [proxy]); - let slice = sliceFromReducer; if (proxyFromReducer !== proxy) { - rerender(); - slice = getSlice(); + rerender(true); + return slice; + } + if (!Object.is(sliceFromReducer, slice)) { + rerender(true); } - return slice; + return sliceFromReducer; }