How to use useReducer Hook in typescript?

Viewed 68

I am converting a React app to Typescript. I have done all changes but I am stuck on useReducer Hook which shows me the error below. I cannot find a solution.

I have tried to pass typescript interfaces different ways in the code below but did not work.

const [messagesState, messagesDispatch] = useReducer(
       messagesReducer,
       messagesInitialState
);

How would you solve this problem?

enter image description here

The error:

  No overload matches this call.
  Overload 1 of 5, '(reducer: ReducerWithoutAction<any>, initializerArg: any, initializer?: undefined): [any, DispatchWithoutAction]', gave the following error.
    Argument of type '(state: IMessagesState, action: IActionWithPayload<IActionsMessages>) => { messages: { messagesData: { message: string | undefined; messageId: number | undefined; messageChatId: number | undefined; messageUserId: number | undefined; messageUserName: string | ... 1 more ... | undefined; timestamp: number | undefined;...' is not assignable to parameter of type 'ReducerWithoutAction<any>'.
  Overload 2 of 5, '(reducer: (state: IMessagesState, action: IActionWithPayload<IActionsMessages>) => { messages: { messagesData: { message: string | undefined; ... 5 more ...; deleted: boolean; }[]; }; }, initialState: never, initializer?: undefined): [...]', gave the following error.
    Argument of type 'IMessagesState' is not assignable to parameter of type 'never'.ts(2769)

Message Initial State:

export interface IMessagesState {
  messages: IMessages;
}
export interface IMessages {
  messagesData: IMessagesData[];
}
export interface IMessagesData {
  message: string;
  messageId: number;
  messageChatId: number;
  messageUserId: number;
  messageUserName: string;
  timestamp: number;
  deleted: boolean;
}
const messagesInitialState: IMessagesState = {
  messages: {
    messagesData: [
      {
        message: "test message",
        messageId: 1,
        messageChatId: 1,
        messageUserId: 1,
        messageUserName: "test",
        timestamp: 1658325608,
        deleted: false,
      },
    ],
  },
};

export default messagesInitialState;

Actions:

export interface IActionWithPayload<T> {
  payload: T;
  type: keyof typeof ACTIONS;
}
export enum ACTIONS {
  ADD_MESSAGE = "ADD_MESSAGE",
}

Message Reducer:

export interface IActionsMessages {
  message?: string;
  messageId?: number;
  messageChatId?: number;
  messageUserId?: number;
  messageUserName?: string | string[];
  timestamp?: number;
  userId?: number;
  newUserName?: string;
  roomId?: number;
}

const messageReducer = (
  state: IMessagesState,
  action: IActionWithPayload<IActionsMessages>
) => {
  switch (action.type) {
    case ACTIONS.ADD_MESSAGE: {
      return {
        ...state,
        messages: {
          ...state.messages,
          messagesData: [
            ...state.messages.messagesData,
            {
              message: action.payload.message,
              messageId: action.payload.messageId,
              messageChatId: action.payload.messageChatId,
              messageUserId: action.payload.messageUserId,
              messageUserName: action.payload.messageUserName,
              timestamp: action.payload.timestamp,
              deleted: false,
            },
          ],
        },
      };
    }
    default:
      return state;
  }
};

export default messageReducer;

State Provider:

export interface IStateContext {
  messagesState: IMessagesState;
  messagesDispatch: React.Dispatch<IActionWithPayload<IActionsMessages>>;
}
export const StateContext = createContext<IStateContext | null>(null);

interface Props {
  children?: ReactNode;
}

export const StateProvider = ({ children }: Props) => {
  const [messagesState, messagesDispatch] = useReducer(
    messagesReducer,
    messagesInitialState
  );

  return (
    <StateContext.Provider
      value={{
        messagesState,
        messagesDispatch,

      }}
    >
      {children}
    </StateContext.Provider>
  );
};
1 Answers

The solution was easier than i could imagine just needed to pass IMessagesState before arrow =>

Thanks HuSaad for clarify.

const messageReducer = (
  state: IMessagesState,
  action: IActionWithPayload<IActionsMessages>
): IMessagesState => {
  switch (action.type) {
    case ACTIONS.ADD_MESSAGE: {
      return {
        ...state,
        messages: {
          ...state.messages,
          messagesData: [
            ...state.messages.messagesData,
            {
              message: action.payload.message,
              messageId: action.payload.messageId,
              messageChatId: action.payload.messageChatId,
              messageUserId: action.payload.messageUserId,
              messageUserName: action.payload.messageUserName,
              timestamp: action.payload.timestamp,
              deleted: false,
            },
          ],
        },
      };
    }
Related