Home > Software engineering >  React/Redux Toolkit TypeScript issue with useDispatch `Expected 0 arguments, but got 1`
React/Redux Toolkit TypeScript issue with useDispatch `Expected 0 arguments, but got 1`

Time:01-31

I am putting together a web app using React/Redux/TypeScript and I don't have a lot of experience with TypeScript. For the register user flow I having an issue in my code where I get the error: Expected 0 arguments, but got 1.ts(2554) when I try to pass the new user form data to the dispatch function to pass to authSlice createAsyncThunk register function.

Below is the code:

authService.ts

import axios from "axios";

const API_URL = "/api/users/";

//Register user
const register = async (userData: {} | null | unknown) => {
  const response = await axios.post(API_URL, userData);

  if (response.data) {
    localStorage.setItem("user", JSON.stringify(response.data));
  }
  return response.data;
};

const authService = {
  register,
};

export default authService;

authSlice.ts

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import authService from "./authService";

export interface InitialState {
  user: {} | null;
  isError: boolean;
  isSuccess: boolean;
  isLoading: boolean;
  message: string | null | unknown;
}

//Get user from localStorage
const user = JSON.parse(localStorage.getItem("user") ?? "{}");

const initialState: InitialState = {
  user: user ? user : null,
  isError: false,
  isSuccess: false,
  isLoading: false,
  message: "",
};

//Register user
export const register = createAsyncThunk(
  "auth/register",
  async (user, thunkAPI) => {
    try {
      return await authService.register(user);
    } catch (error: any) {
      const message =
        (error.response &&
          error.response.data &&
          error.response.data.message) ||
        error.message ||
        error.toString();
      return thunkAPI.rejectWithValue(message);
    }
  }
);

export const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    reset: (state) => {
      state.isLoading = false;
      state.isSuccess = false;
      state.isError = false;
      state.message = "";
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(register.pending, (state) => {
        state.isLoading = true;
      })
      .addCase(register.fulfilled, (state, action) => {
        state.isLoading = false;
        state.isSuccess = true;
        state.user = action.payload;
      })
      .addCase(register.rejected, (state, action) => {
        state.isLoading = false;
        state.isError = true;
        state.message = action.payload;
        state.user = null;
      });
  },
});

export const { reset } = authSlice.actions;
export default authSlice.reducer;

Register.tsx

import React from "react";
import { useState, useEffect } from "react";
import { FaUser } from "react-icons/fa";
import { useSelector, useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import { toast } from "react-toastify";
import { RootState } from "../app/store";
import { useAppSelector, useAppDispatch } from "../app/hooks";
import authService from "../features/auth/authService";
import { register, reset } from "../features/auth/authSlice";

export default function Register() {
  interface FormData {
    name: string;
    email: string;
    password: string;
    password2: string;
  }

  const [formData, setFormData] = useState<FormData>({
    name: "",
    email: "",
    password: "",
    password2: "",
  });

  const { name, email, password, password2 } = formData;

  const navigate = useNavigate();
  const dispatch = useAppDispatch();

  const { user, isLoading, isError, isSuccess, message } = useSelector(
    (state: RootState) => state.auth
  );

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setFormData((prevState) => ({
      ...prevState,
      [e.target.name]: e.target.value,
    }));
  };

  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    console.log(formData);

    if (password !== password2) {
      toast.error("Passwords don't match");
    } else {
      const newUser = {
        name,
        email,
        password,
      };
      dispatch(register(newUser));
    }

    setFormData({
      name: "",
      email: "",
      password: "",
      password2: "",
    });
  };

  return (
    <div className="flex flex-col mt-20 mb-16 mx-auto items-center w-full max-w-2xl border p-6 rounded-md bg-[#f0eff4] shadow-md">
      <section className="flex flex-col items-center text-2xl font-medium mb-12 mt-12 p-4">
        <h1 className="flex items-center text-5xl mb-6 text-[#003049]">
          <FaUser className="flex items-center align-middle mr-4" /> Register
        </h1>
        <p className="text-5xl text-[#003049]">Please create an account</p>
      </section>
      <section className="w-full mb-16">
        <form onSubmit={onSubmit} className="w-7/10 mx-auto">
          <div>
            <input
              type="text"
              id="name"
              name="name"
              value={name}
              placeholder="Enter name"
              onChange={onChange}
              className="w-full p-2.5 border border-gray-300 rounded-md mb-4 font-inherit"
            />
          </div>
          <div>
            <input
              type="email"
              id="email"
              name="email"
              value={email}
              placeholder="Enter email"
              onChange={onChange}
              className="w-full p-2.5 border border-gray-300 rounded-md mb-4 font-inherit"
            />
          </div>
          <div>
            <input
              type="password"
              id="password"
              name="password"
              value={password}
              placeholder="Enter password"
              onChange={onChange}
              className="w-full p-2.5 border border-gray-300 rounded-md mb-4 font-inherit"
            />
          </div>
          <div>
            <input
              type="password"
              id="password2"
              name="password2"
              value={password2}
              placeholder="Re-enter password"
              onChange={onChange}
              className="w-full p-2.5 border border-gray-300 rounded-md mb-4 font-inherit"
            />
          </div>
          <div>
            <button
              type="submit"
              className="py-3 px-4 w-full rounded-md text-white text-center cursor-pointer flex items-center justify-center bg-[#003049] border-[#003049] hover:text-[#f77f00]"
            >
              Submit
            </button>
          </div>
        </form>
      </section>
    </div>
  );
}

The error is occurring in the dispatch(register(newUser)) line. Specifically newUser is showing the error Expected 0 arguments, but got 1.ts(2554)

I have read all of the Redux/Typescript docs and searched this site but could not find a solution that has worked so far.

CodePudding user response:

The issue is most likely that your register thunk does not have a type declared for the user argument.

Currently, it's:

export const register = createAsyncThunk(
  "auth/register",
  async (user, thunkAPI) => {
    // snip body
  }
)

Per our TS usage docs for createAsyncThunk, at a minimum you need to declare a type for the first argument of the payload function - in this case, user:

interface User {
  // fields
}

export const register = createAsyncThunk(
  "auth/register",
  async (user: User, thunkAPI) => {
    // snip body
  }
)
  • Related