1
votes

In react typescript how can I assign to the reducer function two interfaces for the action: standard {type: string, payload: object} and redux-thunk?

reducer.ts

interface IReduxThunkAction {
  (dispatch: any): Promise<void>;
}
interface IActionPayload {
  type?: string;
  payload?: any;
}

type IAction = IActionPayload | IReduxThunkAction;

const reducer = (prevState = initialState, action: IAction): IInitialState => {
  switch (action.type) {
    case UPDATE_ITEMS:
      return updateItemsData(prevState, action.payload);
    default:
      return prevState;
  }
};

Here is giving me that error:

Property 'type' does not exist on type 'IAction'. Property 'type' does not exist on type 'IReduxThunkAction'.

actions.ts

export const getItems = (
  term: string
): ThunkAction<Promise<void>, {}, {}, AnyAction> => {
  return (dispatch: ThunkDispatch<{}, {}, AnyAction>): Promise<void> => {
    return searchItems(term)
      .then(response => {
        const { results } = response.data;
        dispatch(updateItems(results));
      })
      .catch(() => {
        dispatch(fetchError());
      });
  };
};

ItemsList.tsx

// function with useReducer hook inside it

const [reducerState, dispatch] = useReducer(reducer, initialState);

// more code...

const handleSearch = (event: { preventDefault: () => void }) => {
    event.preventDefault();
    if (validateInput(state.value)) {
      dispatch(actions.changeSpinnerState());
      return dispatch(actions.getItems(state.value))
      .then(() =>
        dispatch(actions.changeSpinnerState())
      );
    }
  };

and here it's telling me on line dispatch(actions.getItems(state.value)):

Argument of type 'ThunkAction, {}, {}, AnyAction>' is not assignable to parameter of type 'IAction'. Type 'ThunkAction, {}, {}, AnyAction>' is not assignable to type 'IReduxThunkAction'.

1
Why do you want to assign the second interface to your reducer? Why do you need dispatch inside reducer? - kind user
@kinduser I don't need dispatch inside reducer but if I assign only IActionPayload then dispatch(actions.getItems(state.value)) from the ItemsList.tsx file complains...How can I do it then? - alex
Your getItems func is kind of surprising for me, why do you dispatch a dispatch? Why do you handle side effects in action creator? Haven't seen in my life that kind of approach anywhere. - kind user
@kinduser I editted the code I'm using hooks so it's coming from the useReducer hook - alex

1 Answers

0
votes

Ok I solved it:

ItemsList.tsx

const [reducerState, dispatch] = useReducer(reducer, initialState);

// more code...

const handleSearch = (event: { preventDefault: () => void }) => {
    event.preventDefault();
    if (validateInput(state.value)) {
      dispatch(actions.changeSpinnerState());
      dispatch(actions.changeSpinnerState());
      actions.getItems(state.value)(dispatch as IActionPayload);
    }
  };

actions.ts:

export const getItems = (term: string): React.Dispatch<IActionPayload> => {
  return (dispatch: any): Promise<void> => {
    return searchItems(term)
      .then(response => {
        const { results } = response.data;
        dispatch(actions.changeSpinnerState());
        dispatch(updateItems(results));
      })
      .catch(() => {
        dispatch(fetchError());
      });
  };
};

reducer.ts:

 const reducer = (prevState = initialState,  action: IActionPayload
): IInitialState => {
  switch (action.type) {
    case UPDATE_ITEMS:
      return updateItemsData(prevState, action.payload);
    default:
      return prevState;
  }
};