Last active
November 27, 2020 00:42
-
-
Save artalar/55633a46b8a69146a31a053bdc9630eb to your computer and use it in GitHub Desktop.
This file contains hidden or 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
declare const IS_ACTION_FETCH: unique symbol | |
/** | |
* Fetch fabric with statuses actions | |
* @param {(payload: Payload) => Promise<Result>} fetcher | |
* @param {{ | |
* onDone?: (result: Result, store: Store) => void | |
* onFail?: (error: unknown, store: Store) => void | |
* }} hooks | |
* @returns {{ | |
* (payload: Payload): { | |
* payload: Payload | |
* type: string | |
* } | |
* done: PayloadActionCreator<Result> | |
* fail: PayloadActionCreator<unknown> | |
* isPending: Atom<boolean> | |
* cast(store: Store, payload: Payload): Promise<Result> | |
* }} | |
* | |
* @example | |
* ```ts | |
* const fetchUser = declareActionFetch<User, string>((id: string) => fetch(`/api/user/${id}`)) | |
* // ... | |
* export const initUser = declareAction(async (id, store) => { | |
* try { | |
* const user = await fetchUser.cast(store, id) // user: User | |
* } catch(e) { | |
* if (e.code === 403) store.dispatch(goTo(ROUTES.signIn)) | |
* } | |
* }) | |
* | |
* export const userAtom = declareAtom<null | User>(null, on => [ | |
* on(fetchUser.done, (state, user) => user), | |
* ]) | |
* ``` | |
*/ | |
export function declareActionFetch<Result, Payload = undefined>( | |
fetcher: (payload: Payload) => Promise<Result>, | |
{ | |
onDone = noop, | |
onFail = noop, | |
}: { | |
onDone?: (result: Result, store: Store) => void | |
onFail?: (error: unknown, store: Store) => void | |
} = { | |
onDone: noop, | |
onFail: noop, | |
}, | |
): { | |
(payload: Payload): { | |
payload: Payload | |
type: string | |
} | |
done: PayloadActionCreator<Result> | |
fail: PayloadActionCreator<unknown> | |
isPending: Atom<boolean> | |
cast(store: Store, payload: Payload): Promise<Result> | |
[IS_ACTION_FETCH]: true | |
} { | |
const fetchAction: any = declareAction<Payload>( | |
'fetch call', | |
(payload, store) => | |
fetcher(payload) | |
.then(response => { | |
store.dispatch(fetchAction.done(response)) | |
return response | |
}) | |
.catch(error => { | |
store.dispatch(fetchAction.fail(error)) | |
throw error | |
}), | |
) | |
const done = declareAction<Result>('fetch done', onDone) | |
const fail = declareAction<unknown>('fetch fail', onFail) | |
const isPending = declareAtom(false, on => [ | |
on(fetchAction, () => true), | |
on(done, () => false), | |
on(fail, () => false), | |
]) | |
const cast = (store: Store, payload: Payload) => { | |
let res: any | |
let rej: any | |
const action = fetchAction(payload) | |
store.dispatch({ | |
...action, | |
reactions: [ | |
(...a) => | |
action.reactions[0](...a).then( | |
payload => res(payload), | |
error => rej(error), | |
), | |
], | |
}) | |
return new Promise((_res, _rej) => { | |
res = _res | |
rej = _rej | |
}) | |
} | |
fetchAction.done = done | |
fetchAction.fail = fail | |
fetchAction.isPending = isPending | |
fetchAction.cast = cast | |
fetchAction[IS_ACTION_FETCH] = true | |
return fetchAction | |
} |
This file contains hidden or 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
// NORMALIZATION FABRIC EXAMPLE | |
import { declareAction, declareAtom } from '@reatom/core'; | |
/** | |
* | |
* @param key | |
* @example | |
* ```ts | |
* // here `updateElement` will NOT trigger List rerender | |
* // that is good for performance | |
* | |
* function Element({ id }) { | |
* const el = useAtom(mapAtom, (map) => map[id], [id]); | |
* | |
* return <Component {...el} />; | |
* } | |
* | |
* function List() { | |
* const ids = useAtom(idsAtom); | |
* | |
* return ids.map((id) => <Element id={id} />); | |
* } | |
* ``` | |
*/ | |
export function declareNormAtoms< | |
T extends Record<string, any>, | |
Key extends keyof T | |
>(key: Key) { | |
const update = declareAction<T[]>(); | |
const updateElement = declareAction<{ | |
id: string; | |
element: Partial<T>; | |
}>(); | |
const deleteElement = declareAction<string>(); | |
const idsAtom = declareAtom<string[]>([], (on) => [ | |
on(update, (state, payload) => payload.map((el) => el[key])), | |
on(deleteElement, (state, id) => state.filter((_id) => _id !== id)), | |
]); | |
const mapAtom = declareAtom<Record<string, T>>({}, (on) => [ | |
on(update, (state, payload) => | |
payload.reduce<Record<string, T>>((acc, el) => { | |
const id = el[key]; | |
acc[id] = el; | |
return acc; | |
}, {}) | |
), | |
on(updateElement, (state, { id, element }) => ({ | |
...state, | |
[id]: { | |
...state[id], | |
...element, | |
}, | |
})), | |
on(deleteElement, (state, id) => { | |
const { [id]: _deleted, ...newState } = state; | |
return newState; | |
}), | |
]); | |
return { | |
update, | |
updateElement, | |
deleteElement, | |
idsAtom, | |
mapAtom, | |
}; | |
} |
This file contains hidden or 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
import { Atom, declareAtom, getState } from '@reatom/core' | |
export function noop() {} | |
/** | |
* @example | |
* ```js | |
* const listIdsAtom = filter( | |
* map(listAtom, list => list.map(({id}) => id)), | |
* (ids, nextIds) => ids.some( | |
* (id, i) => id !== nextIds[i] | |
* ) | |
* ) | |
* ``` | |
*/ | |
export function filter<T>( | |
atom: Atom<T>, | |
comparator: (state: T, nextState: T) => boolean, | |
): Atom<T> { | |
return declareAtom(getState(atom(), atom)!, on => [ | |
on(atom, (state, nextState) => | |
// @ts-ignore | |
comparator(state, nextState) ? nextState : state, | |
), | |
]) | |
} |
This file contains hidden or 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
import { Atom, declareAtom, combine, getState, map } from '@reatom/core' | |
export function noop() {} | |
export function prev<T>(atom: Atom<T>): Atom<T> | |
export function prev<I, O>(atom: Atom<I>, mapper: (state: I) => O): Atom<O> | |
export function prev<T>(atom: Atom<T>, mapper: (state: T) => T = v => v) { | |
const initState = getState(atom(), atom) | |
return map( | |
declareAtom([initState, initState], on => [ | |
on(atom, (state, value) => [state[1], value]), | |
]), | |
cache => mapper(cache[0]!), | |
) | |
} |
This file contains hidden or 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
/* model.js */ | |
// static workflow | |
export const changeInput = declareAction() | |
export const inputAtom = declareAtom('', on => [ | |
on(changeInput, (_, input) => input), | |
]) | |
// lifetime workflow | |
export const init = declareAction(store => { | |
const ws = new Socket(url) | |
const initInput = store.getState(inputAtom) | |
ws.on('@init', input => { | |
if (store.getState(inputAtom) === initInput) | |
store.dispatch(changeInput(input)) | |
}) | |
const unsubscribe = store.subscribe(inputAtom, input => | |
ws.send('input', input), | |
) | |
store.subscribe(cleanup, () => { | |
ws.disconnect() | |
unsubscribe() | |
}) | |
}) | |
export const cleanup = declareAction() | |
/* view.js */ | |
const onInit = useAction(init) | |
const onCleanup = useAction(cleanup) | |
useEffect(() => { | |
onInit() | |
return () => onCleanup() | |
}, []) |
This file contains hidden or 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
/** | |
* Adds the possibility of waiting the action dispatch | |
* @param store | |
* @param actionCreator | atom | |
* @example | |
* const result = await take(store, myAction) | |
*/ | |
export function take<T>( | |
store: Store, | |
target: PayloadActionCreator<T> | Atom<T>, | |
): Promise<T> { | |
let res: Function | |
const promise = new Promise(r => (res = r)) | |
const unsubscribe = store.subscribe(target, value => { | |
unsubscribe() | |
res(value) | |
}) | |
return promise as any | |
} |
This file contains hidden or 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
import { declareAtom, getTree } from "@reatom/core"; | |
/** | |
* the same as effector.sample | |
* @param source atom for receive it data | |
* @param reason atom or action thats trigger data receiving | |
* @returns atom with source atom state | |
* | |
* @example | |
* ```js | |
* const validationAtom = declareAtom( | |
* on(trigger(formAtom, onBlur), (state, formData) => '...') | |
* ) | |
* ``` | |
*/ | |
export const trigger = ( | |
source, // atom | |
reason // atom or action | |
) => { | |
const defaultState = source()[getTree(source).id]; | |
const accumulatorMetaAtom = declareAtom( | |
{ | |
data: defaultState, | |
shouldUpdate: false | |
}, | |
on => [ | |
on(source, (state, data) => ({ data, shouldUpdate: false })), | |
on(reason, ({ data }) => ({ data, shouldUpdate: true })) | |
] | |
); | |
return declareAtom(defaultState, on => [ | |
on(accumulatorMetaAtom, (state, { data, shouldUpdate }) => | |
shouldUpdate ? data : state | |
) | |
]); | |
}; |
Author
artalar
commented
Dec 23, 2019
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment