1 import type { ActionReducerMapBuilder, Draft, ThunkDispatch } from '@reduxjs/toolkit';
2 import { type Action, createAction, miniSerializeError } from '@reduxjs/toolkit';
3 import type { ThunkAction } from 'redux-thunk';
5 import { getFetchedAt as getDefaultFetchedAt, getFetchedEphemeral } from './fetchedAt';
6 import type { ReducerValue, ThunkOptions } from './interface';
7 import { cacheHelper, createPromiseStore } from './promiseStore';
9 export const createAsyncModelThunk = <Returned, State, Extra, ThunkArg = void>(
18 dispatch: ThunkDispatch<State, Extra, Action>;
19 getState: () => State;
21 options?: ThunkOptions<ThunkArg>;
22 }) => Promise<Returned>;
24 dispatch: ThunkDispatch<State, Extra, Action>;
25 getState: () => State;
27 options?: ThunkOptions<ThunkArg>;
28 }) => ReducerValue<Returned> | undefined;
31 const pending = createAction(`${prefix}/pending`, () => ({
35 const fulfilled = createAction(`${prefix}/fulfilled`, (payload: Returned) => ({
39 const rejected = createAction(`${prefix}/failed`, (payload: any) => ({
43 const promiseStore = createPromiseStore<Returned>();
46 options?: ThunkOptions<ThunkArg>
51 ReturnType<typeof pending> | ReturnType<typeof fulfilled> | ReturnType<typeof rejected>
53 return (dispatch, getState, extraArgument) => {
54 const select = () => {
55 return previous({ dispatch, getState, extraArgument, options });
57 const cb = async () => {
60 const value = await miss({ dispatch, getState, extraArgument, options });
61 dispatch(fulfilled(value));
64 dispatch(rejected(miniSerializeError(error)));
68 return cacheHelper({ store: promiseStore, select, cb, cache: options?.cache, expiry });
80 export const handleAsyncModel = <Returned, State, Extra, Options>(
81 builder: ActionReducerMapBuilder<ReducerValue<Returned>>,
82 cases: ReturnType<typeof createAsyncModelThunk<Returned, State, Extra, Options>>,
86 getFetchedAt: typeof getDefaultFetchedAt;
87 } = { getFetchedAt: getDefaultFetchedAt }
90 .addCase(cases.pending, (state) => {
91 state.error = undefined;
93 .addCase(cases.fulfilled, (state, action) => {
94 state.value = action.payload as Draft<Returned> | undefined;
95 state.error = undefined;
96 state.meta.fetchedAt = getFetchedAt();
97 state.meta.fetchedEphemeral = getFetchedEphemeral();
99 .addCase(cases.rejected, (state, action) => {
100 state.error = action.payload;
101 state.meta.fetchedAt = getFetchedAt();
105 export const previousSelector =
106 <Returned, State, Extra, ThunkArg = void>(
107 selector: (state: State) => ReducerValue<Returned>
109 dispatch: ThunkDispatch<State, Extra, Action>;
110 getState: () => State;
111 extraArgument: Extra;
112 options?: ThunkOptions<ThunkArg>;
113 }) => ReducerValue<Returned> | undefined) =>
115 return selector(getState());
118 export const selectPersistModel = <T>(state: ReducerValue<T>) => {
119 if (state.error || state.value === undefined) {