React Redux Actions Reducer Saga Implementation Pattern - mosinn/DOCS-n-Snippets-n-Steps GitHub Wiki

UI API call approaches:

  1. Create Response *.ts type file or export eg. account.ts
  2. Store.ts -> Add "placeholder key" for Success Response Payload to 1 e.g. store.ts, specifying type as 1
  3. Action -> e.g. myFeature1.action.ts
    1. 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
    2. 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
  4. Reducer -> e.g. myFeature1.reducer.ts
    1. 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.
  5. Saga -> e.g. myFeature1.saga.ts
    1. Listens on 1 items:
    2. API_FETCH_<<>>_INIT makes API call
    3. Inside API call success of above, dispatch API_FETCH_<<>>_SUCCESS

  1. account.ts
/**
 * Data model from the API response.
 */
export type Account = {
  id: string;
  currency: string;
  acctDisplay: string;
  links?: Link[];
};
  1. store.ts
export interface MyStore {
  key1?: {
    data: Type1;
  };
  key2: {
    data: Type2[];
    isLoaded: boolean;
  };

  myReduxAccounts?: {
    data: Account[];
    isLoaded: boolean;
  };
  ....
  1. 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,
    ...
}

  1. 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 } };

    ...

  }

  1. 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);
  }
}


⚠️ **GitHub.com Fallback** ⚠️