E
E
Evgeny Zhurov2022-03-22 01:52:46
typescript
Evgeny Zhurov, 2022-03-22 01:52:46

How to type useReducer and are there generally cleaner options?

I'm experimenting with useReducer and ran into this problem. For example, there is an array of some records. You need to get it, assign it to the component state, and then manually add new entries and delete them. The question is how to properly type this whole thing?

I sketched out such an option , but a few things confuse me:

First, the explicit assignment of the three possible types in payload:

type ActionType = {
  type: ActionPointsType;
  payload: AlbumType | AlbumType[] | number;
};


Secondly, the explicit indication of types in the return of the reducer function (all these "as AlbumType[]"):
switch (action.type) {
    case ActionPoints.SET_ALBUMS:
      return action.payload as AlbumType[];
    case ActionPoints.ADD_ALBUM:
      return [...state, action.payload] as AlbumType[];
    case ActionPoints.REMOVE_ALBUM:
      return state.filter((album) => album.id !== action.payload);
    default:
      return state;
  }


In general, what is the best way to type reducer function and ActionType fields? Surely you can somehow use generics here, but I can’t figure out how exactly. I tried, for example, like this:
type ActionType<T = any> = {
  type: ActionPointsType
  payload: T
};

// ...
const addNewAlbum = (event: React.FormEvent) => {
    event.preventDefault();

    const action: ActionType<AlbumType> = {
      type: ActionPoints.ADD_ALBUM,
      payload: {
        id: albums.length + 1,
        title: newAlbum
      }
    }

    dispatch(action);
  }


There are no errors, but action.payload inside the reducer function, as well as albums in the state, instantly turn into any. I tried a couple more options, but everywhere some of their jambs pop up.

The second question, abstracting from typing, is how normal is my version in general, as they say? Maybe there are some more correct or elegant ways to implement a similar task?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
D
Dmitry Belyaev, 2022-03-22
@Zhuroff

type AlbumType = {
  id: number;
  title: string;
};

enum ActionPoints {
  SET_ALBUMS = "SET_ALBUMS",
  ADD_ALBUM = "ADD_ALBUM",
  REMOVE_ALBUM = "REMOVE_ALBUM"
}

type ActionType = {
  type: ActionPoints.SET_ALBUMS;
  payload: AlbumType[];
} | {
  type: ActionPoints.ADD_ALBUM;
  payload: AlbumType;
} | {
  type: ActionPoints.REMOVE_ALBUM;
  payload: number;
};

const reducer = (state: AlbumType[], action: ActionType) => {
  switch (action.type) {
    case ActionPoints.SET_ALBUMS:
      return action.payload;
    case ActionPoints.ADD_ALBUM:
      return [...state, action.payload];
    case ActionPoints.REMOVE_ALBUM:
      return state.filter((album) => album.id !== action.payload);
    default:
      return state;
  }
};

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question