Skip to content

Commit

Permalink
feat: add useAtomValue hook
Browse files Browse the repository at this point in the history
  • Loading branch information
dai-shi committed Aug 16, 2024
1 parent 7f73408 commit 639f5b3
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased]

### Added

- `useAtomValue` that doesn't resolve promises

## [0.3.0] - 2024-05-24

### Changed
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { atomWithPending } from './atomWithPending.js';
export { usePrepareAtoms } from './usePrepareAtoms.js';
export { useAtomValue } from './useAtomValue.js';
70 changes: 70 additions & 0 deletions src/useAtomValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useDebugValue, useEffect, useReducer } from 'react';
import type { ReducerWithoutAction } from 'react';
import type { Atom, ExtractAtomValue } from 'jotai/vanilla';
import { useStore } from 'jotai/react';

// Unlike useAtomValue from 'jotai/react',
// this hook doesn't resolve promises.

type Store = ReturnType<typeof useStore>;

type Options = Parameters<typeof useStore>[0] & {
delay?: number;
};

export function useAtomValue<Value>(
atom: Atom<Value>,
options?: Options,
): Value;

export function useAtomValue<AtomType extends Atom<unknown>>(
atom: AtomType,
options?: Options,
): ExtractAtomValue<AtomType>;

export function useAtomValue<Value>(atom: Atom<Value>, options?: Options) {
const store = useStore(options);

const [[valueFromReducer, storeFromReducer, atomFromReducer], rerender] =
useReducer<
ReducerWithoutAction<readonly [Value, Store, typeof atom]>,
undefined
>(
(prev) => {
const nextValue = store.get(atom);
if (
Object.is(prev[0], nextValue) &&
prev[1] === store &&
prev[2] === atom
) {
return prev;
}
return [nextValue, store, atom];
},
undefined,
() => [store.get(atom), store, atom],
);

let value = valueFromReducer;
if (storeFromReducer !== store || atomFromReducer !== atom) {
rerender();
value = store.get(atom);
}

const delay = options?.delay;
useEffect(() => {
const unsub = store.sub(atom, () => {
if (typeof delay === 'number') {
// delay rerendering to wait a promise possibly to resolve
setTimeout(rerender, delay);
return;
}
rerender();
});
rerender();
return unsub;
}, [store, atom, delay]);

useDebugValue(value);
return value;
}

0 comments on commit 639f5b3

Please sign in to comment.