- Create Response *.ts type file or export eg. account.ts
- Store.ts -> Add "placeholder key" for Success Response Payload to 1 e.g. store.ts, specifying type as 1
- Action -> e.g. myFeature1.action.ts
- API_FETCH_<<>>_INIT
a. takes 'undefined' payload type
b. Not needed in reducer as no state is modified
c. Note:
i. Used in Saga as that just listens to this TYPE to initiate API call
- API_FETCH_<<>>_SUCCESS
a. takes 'ResponseModel' payload type
b. Note:
i. Define "handling" in reducer to update State chunk "placeholder key" with 'ResponseModel' received
ii. Define "triggering" in Saga i.e. dispatch on API call success
- Reducer -> e.g. myFeature1.reducer.ts
- Inside switch case for action type: API_FETCH_<<>>_SUCCESS, update relevant state chunk with incoming payload (which will later be spitted out by "API call success" inside saga.
- Saga -> e.g. myFeature1.saga.ts
- Listens on 1 items:
- API_FETCH_<<>>_INIT makes API call
- Inside API call success of above, dispatch API_FETCH_<<>>_SUCCESS
- account.ts
/**
* Data model from the API response.
*/
export type Account = {
id: string;
currency: string;
acctDisplay: string;
links?: Link[];
};
- store.ts
export interface MyStore {
key1?: {
data: Type1;
};
key2: {
data: Type2[];
isLoaded: boolean;
};
myReduxAccounts?: {
data: Account[];
isLoaded: boolean;
};
....
- myFeature1.action.ts
...
export enum MyFeature1ActionType {
...
FETCH_API_ACCOUNTS_INIT = 'FETCH_ACCOUNTS',
FETCH_API_ACCOUNTS_SUCCESS = 'FETCH_ACCOUNTS_SUCCESS',
...
}
const fetchAccountsInitAction = createAction(MyFeature1ActionType.FETCH_ACCOUNTS_INIT)<undefined>();
const fetchAccountsSuccessAction = createAction(MyFeature1ActionType.FETCH_ACCOUNTS_SUCCESS)<Account[]>();
export const myFeature1Actions = {
...
fetchAccounts,
fetchAccountsSuccess,
...
}
- myFeature1.reducer.ts
export const myFeature1Reducer: Reducer<MyFeature1Store, MyFeature1Action> = (
state = initialState,
action
): MyFeature1Store => {
switch (action.type) {
...
//case MyFeature1ActionType.FETCH_ACCOUNTS:
case MyFeature1ActionType.FETCH_ACCOUNTS_SUCCESS:
return { ...state, myReduxAccounts: { ...state.myReduxAccounts, data: action.payload, isLoaded: true } };
...
}
- myFeature1.saga.ts
export default function* watcherSaga() {
...
// Listen to INIT call e.g. from UI button click
yield takeLatest(MyFeature1ActionType.FETCH_ACCOUNTS, fetchAccountsWorker);
...
}
export function* fetchAccountsWorker(action: ActionType<typeof myFeature1Actions.fetchAccounts>) {
try {
yield put(spinnerActions.start());
const authStore: AuthStore = yield select(authSelector);
const response: AxiosResponse<Account[]> = yield call(fetchAccounts, <<getUrl()>>, authStore.token, action);
yield put(myFeature1Actions.fetchAccountsSuccess(uiAccountsModelFromApiAccountResponse(response.data)));
yield put(spinnerActions.end());
} catch (e) {
// Possibly dispatch Error toast showing action
console.error('Failed to fetch accounts', e);
}
}