-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(jotai): add jotai for local reference
- Loading branch information
Showing
33 changed files
with
2,677 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './vanilla' | ||
export * from './react' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const MODE: 'development' | 'production' = 'development' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export { Provider, useStore } from './react/Provider' | ||
export { useAtomValue } from './react/useAtomValue' | ||
export { useSetAtom } from './react/useSetAtom' | ||
export { useAtom } from './react/useAtom' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { createContext, createElement, useContext, useRef } from 'react' | ||
import type { FunctionComponentElement, ReactNode } from 'react' | ||
import { createStore, getDefaultStore } from '../vanilla' | ||
|
||
type Store = ReturnType<typeof createStore> | ||
|
||
type StoreContextType = ReturnType<typeof createContext<Store | undefined>> | ||
const StoreContext: StoreContextType = createContext<Store | undefined>( | ||
undefined, | ||
) | ||
|
||
type Options = { | ||
store?: Store | ||
} | ||
|
||
export const useStore = (options?: Options): Store => { | ||
const store = useContext(StoreContext) | ||
return options?.store || store || getDefaultStore() | ||
} | ||
|
||
export const Provider = ({ | ||
children, | ||
store, | ||
}: { | ||
children?: ReactNode | ||
store?: Store | ||
}): FunctionComponentElement<{ value: Store | undefined }> => { | ||
const storeRef = useRef<Store>() | ||
if (!store && !storeRef.current) { | ||
storeRef.current = createStore() | ||
} | ||
return createElement( | ||
StoreContext.Provider, | ||
{ | ||
value: store || storeRef.current, | ||
}, | ||
children, | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import type { | ||
Atom, | ||
ExtractAtomArgs, | ||
ExtractAtomResult, | ||
ExtractAtomValue, | ||
PrimitiveAtom, | ||
SetStateAction, | ||
WritableAtom, | ||
} from '../vanilla' | ||
import { useAtomValue } from './useAtomValue' | ||
import { useSetAtom } from './useSetAtom' | ||
|
||
type SetAtom<Args extends unknown[], Result> = (...args: Args) => Result | ||
|
||
type Options = Parameters<typeof useAtomValue>[1] | ||
|
||
export function useAtom<Value, Args extends unknown[], Result>( | ||
atom: WritableAtom<Value, Args, Result>, | ||
options?: Options, | ||
): [Awaited<Value>, SetAtom<Args, Result>] | ||
|
||
export function useAtom<Value>( | ||
atom: PrimitiveAtom<Value>, | ||
options?: Options, | ||
): [Awaited<Value>, SetAtom<[SetStateAction<Value>], void>] | ||
|
||
export function useAtom<Value>( | ||
atom: Atom<Value>, | ||
options?: Options, | ||
): [Awaited<Value>, never] | ||
|
||
export function useAtom< | ||
AtomType extends WritableAtom<unknown, never[], unknown>, | ||
>( | ||
atom: AtomType, | ||
options?: Options, | ||
): [ | ||
Awaited<ExtractAtomValue<AtomType>>, | ||
SetAtom<ExtractAtomArgs<AtomType>, ExtractAtomResult<AtomType>>, | ||
] | ||
|
||
export function useAtom<AtomType extends Atom<unknown>>( | ||
atom: AtomType, | ||
options?: Options, | ||
): [Awaited<ExtractAtomValue<AtomType>>, never] | ||
|
||
export function useAtom<Value, Args extends unknown[], Result>( | ||
atom: Atom<Value> | WritableAtom<Value, Args, Result>, | ||
options?: Options, | ||
) { | ||
return [ | ||
useAtomValue(atom, options), | ||
// We do wrong type assertion here, which results in throwing an error. | ||
useSetAtom(atom as WritableAtom<Value, Args, Result>, options), | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
/// <reference types="react/experimental" /> | ||
import ReactExports, { useDebugValue, useEffect, useReducer } from 'react' | ||
import type { ReducerWithoutAction } from 'react' | ||
import type { Atom, ExtractAtomValue } from '../vanilla' | ||
import { useStore } from './Provider' | ||
import { MODE } from '../mode' | ||
|
||
type Store = ReturnType<typeof useStore> | ||
|
||
const isPromiseLike = (x: unknown): x is PromiseLike<unknown> => | ||
typeof (x as any)?.then === 'function' | ||
|
||
const attachPromiseMeta = <T>( | ||
promise: PromiseLike<T> & { | ||
status?: 'pending' | 'fulfilled' | 'rejected' | ||
value?: T | ||
reason?: unknown | ||
}, | ||
) => { | ||
promise.status = 'pending' | ||
promise.then( | ||
(v) => { | ||
promise.status = 'fulfilled' | ||
promise.value = v | ||
}, | ||
(e) => { | ||
promise.status = 'rejected' | ||
promise.reason = e | ||
}, | ||
) | ||
} | ||
|
||
const use = | ||
ReactExports.use || | ||
(<T>( | ||
promise: PromiseLike<T> & { | ||
status?: 'pending' | 'fulfilled' | 'rejected' | ||
value?: T | ||
reason?: unknown | ||
}, | ||
): T => { | ||
if (promise.status === 'pending') { | ||
throw promise | ||
} else if (promise.status === 'fulfilled') { | ||
return promise.value as T | ||
} else if (promise.status === 'rejected') { | ||
throw promise.reason | ||
} else { | ||
attachPromiseMeta(promise) | ||
throw promise | ||
} | ||
}) | ||
|
||
const continuablePromiseMap = new WeakMap< | ||
PromiseLike<unknown>, | ||
Promise<unknown> | ||
>() | ||
|
||
const createContinuablePromise = <T>(promise: PromiseLike<T>) => { | ||
let continuablePromise = continuablePromiseMap.get(promise) | ||
if (!continuablePromise) { | ||
continuablePromise = new Promise<T>((resolve, reject) => { | ||
let curr = promise | ||
const onFulfilled = (me: PromiseLike<T>) => (v: T) => { | ||
if (curr === me) { | ||
resolve(v) | ||
} | ||
} | ||
const onRejected = (me: PromiseLike<T>) => (e: unknown) => { | ||
if (curr === me) { | ||
reject(e) | ||
} | ||
} | ||
const registerCancelHandler = (p: PromiseLike<T>) => { | ||
if ('onCancel' in p && typeof p.onCancel === 'function') { | ||
p.onCancel((nextValue: PromiseLike<T> | T) => { | ||
if (MODE !== 'production' && nextValue === p) { | ||
throw new Error('[Bug] p is not updated even after cancelation') | ||
} | ||
if (isPromiseLike(nextValue)) { | ||
continuablePromiseMap.set(nextValue, continuablePromise!) | ||
curr = nextValue | ||
nextValue.then(onFulfilled(nextValue), onRejected(nextValue)) | ||
registerCancelHandler(nextValue) | ||
} else { | ||
resolve(nextValue) | ||
} | ||
}) | ||
} | ||
} | ||
promise.then(onFulfilled(promise), onRejected(promise)) | ||
registerCancelHandler(promise) | ||
}) | ||
continuablePromiseMap.set(promise, continuablePromise) | ||
} | ||
return continuablePromise | ||
} | ||
|
||
type Options = Parameters<typeof useStore>[0] & { | ||
delay?: number | ||
} | ||
|
||
export function useAtomValue<Value>( | ||
atom: Atom<Value>, | ||
options?: Options, | ||
): Awaited<Value> | ||
|
||
export function useAtomValue<AtomType extends Atom<unknown>>( | ||
atom: AtomType, | ||
options?: Options, | ||
): Awaited<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') { | ||
const value = store.get(atom) | ||
if (isPromiseLike(value)) { | ||
attachPromiseMeta(createContinuablePromise(value)) | ||
} | ||
// delay rerendering to wait a promise possibly to resolve | ||
setTimeout(rerender, delay) | ||
return | ||
} | ||
rerender() | ||
}) | ||
rerender() | ||
return unsub | ||
}, [store, atom, delay]) | ||
|
||
useDebugValue(value) | ||
// The use of isPromiseLike is to be consistent with `use` type. | ||
// `instanceof Promise` actually works fine in this case. | ||
if (isPromiseLike(value)) { | ||
const promise = createContinuablePromise(value) | ||
return use(promise) | ||
} | ||
return value as Awaited<Value> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { useCallback } from 'react' | ||
import type { | ||
ExtractAtomArgs, | ||
ExtractAtomResult, | ||
WritableAtom, | ||
} from '../vanilla' | ||
import { useStore } from './Provider' | ||
import { MODE } from '../mode' | ||
|
||
type SetAtom<Args extends unknown[], Result> = (...args: Args) => Result | ||
type Options = Parameters<typeof useStore>[0] | ||
|
||
export function useSetAtom<Value, Args extends unknown[], Result>( | ||
atom: WritableAtom<Value, Args, Result>, | ||
options?: Options, | ||
): SetAtom<Args, Result> | ||
|
||
export function useSetAtom< | ||
AtomType extends WritableAtom<unknown, never[], unknown>, | ||
>( | ||
atom: AtomType, | ||
options?: Options, | ||
): SetAtom<ExtractAtomArgs<AtomType>, ExtractAtomResult<AtomType>> | ||
|
||
export function useSetAtom<Value, Args extends unknown[], Result>( | ||
atom: WritableAtom<Value, Args, Result>, | ||
options?: Options, | ||
) { | ||
const store = useStore(options) | ||
const setAtom = useCallback( | ||
(...args: Args) => { | ||
if (MODE !== 'production' && !('write' in atom)) { | ||
// useAtom can pass non writable atom with wrong type assertion, | ||
// so we should check here. | ||
throw new Error('not writable atom') | ||
} | ||
return store.set(atom, ...args) | ||
}, | ||
[store, atom], | ||
) | ||
return setAtom | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export { useResetAtom } from './utils/useResetAtom' | ||
export { useReducerAtom } from './utils/useReducerAtom' | ||
export { useAtomCallback } from './utils/useAtomCallback' | ||
export { useHydrateAtoms } from './utils/useHydrateAtoms' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { useMemo } from 'react' | ||
import { useSetAtom } from '../useSetAtom' | ||
import { atom } from '../../vanilla' | ||
import type { Getter, Setter } from '../../vanilla' | ||
|
||
type Options = Parameters<typeof useSetAtom>[1] | ||
|
||
export function useAtomCallback<Result, Args extends unknown[]>( | ||
callback: (get: Getter, set: Setter, ...arg: Args) => Result, | ||
options?: Options, | ||
): (...args: Args) => Result { | ||
const anAtom = useMemo( | ||
() => atom(null, (get, set, ...args: Args) => callback(get, set, ...args)), | ||
[callback], | ||
) | ||
return useSetAtom(anAtom, options) | ||
} |
Oops, something went wrong.