【问题标题】:Typescript error TS2554: Expected 0 arguments, but got 1. while using useReducer and useContext with React打字稿错误 TS2554:预期 0 个参数,但得到 1 个。同时使用 useReducer 和 useContext 和 React
【发布时间】:2021-07-06 12:22:49
【问题描述】:

我有许多 useReducers 和 useContext ,我遇到了与下面列出的错误 (TS2554) 类似的错误。 我选择了 AuthReducer,因为它是最简单的。 对于每个 Action 调度,我都会收到相同的错误。我玩弄了其他解决方案并尝试了它们,添加了更多定义,但显然我没有做我应该做的事!

这是我使用 Typescript 进行的第一个项目,所以请放轻松!

据我所知,Dispatch 不期待任何参数,但正在接收参数。但是,当我声明我的 AuthReducer 时,它有一个 Actions 类型的动作参数并返回 AuthState。

/AuthState.tsx:40:16 - error TS2554: Expected 0 arguments, but got 1.

40       dispatch({
41         type: USER_LOADED,
42         payload: res.data,
43       });

AuthReducer.tsx

import {
  USER_LOADED,
  AUTH_ERROR,
  LOGIN_SUCCESS,
  LOGIN_FAIL,
  LOGOUT,
  CLEAR_ERRORS,
} from '../types';
import { initialState } from './AuthState';

type Actions =
  | {
      type: 'USER_LOADED';
      payload: {
        name: string;
        id: string;
      };
    }
  | {
      type: 'AUTH_ERROR';
      payload?: string;
    }
  | {
      type: 'LOGIN_SUCCESS';
      payload: { token: string };
    }
  | {
      type: 'LOGIN_FAIL';
      payload: string;
    }
  | {
      type: 'LOGOUT';
      payload?: string;
    }
  | {
      type: 'CLEAR_ERRORS';
    };

interface AuthState {
  token?: string | null;
  isAuthenticated?: boolean;
  loading: boolean;
  user?: {
    name: string;
    id: string;
  } | null;
  error?: string | undefined | null;
}

const AuthReducer = (
  state: typeof initialState,
  action: Actions
): AuthState => {
  switch (action.type) {
    case USER_LOADED:
      return {
        ...state,
        isAuthenticated: true,
        loading: false,
        user: action.payload,
      };
    case LOGIN_SUCCESS:
      localStorage.setItem('token', action.payload.token);
      return {
        ...state,
        ...action.payload,
        isAuthenticated: true,
        loading: false,
      };
    case AUTH_ERROR:
    case LOGIN_FAIL:
    case LOGOUT:
      localStorage.removeItem('token');
      return {
        ...state,
        token: null,
        isAuthenticated: false,
        loading: false,
        user: null,
        error: action.payload,
      };
    case CLEAR_ERRORS:
      return {
        ...state,
        error: null,
      };
    default:
      return state;
  }
};

export default AuthReducer;

AuthContext.tsx

import { createContext } from 'react';
import { initialState } from './AuthState';

type authContextType = {
  loadUser: () => Promise<void> | null;
  login: (formData: {
    email: string;
    password: string;
  }) => Promise<void> | null;
  logout: () => void;
  clearErrors: () => void;

  error: string | null;
  isAuthenticated: boolean;
  loading: boolean;
  user: {
    name: string;
    id: string;
  };
  token: string;
};

const authContext = createContext<authContextType>(initialState); //TODO A more robust type is possible

export default authContext;

AuthState.tsx

import React, { useReducer } from 'react';
import axios from 'axios';
import AuthContext from './AuthContext';
import AuthReducer from './AuthReducer';
import setAuthToken from '../../utils/setAuthToken';
import {
  USER_LOADED,
  AUTH_ERROR,
  LOGIN_SUCCESS,
  LOGIN_FAIL,
  LOGOUT,
  CLEAR_ERRORS,
} from '../types';

export const initialState = {
  loadUser: null,
  login: null,
  logout: () => {},
  clearErrors: () => {},

  token: localStorage.getItem('token'),
  isAuthenticated: null,
  loading: true,
  error: null,
  user: null,
};

const AuthState: React.FC = ({ children }) => {
  const [state, dispatch] = useReducer(AuthReducer, initialState);

  //Load User
  const loadUser = async () => {
    if (localStorage.token) {
      setAuthToken(localStorage.token);
    }

    try {
      const res = await axios.get('api/auth');

      dispatch({
        type: USER_LOADED,
        payload: res.data,
      });
    } catch (err) {
      dispatch({ type: AUTH_ERROR });
    }
  };
  //Login User
  const login = async (formData: { email: string; password: string }) => {
    const config = {
      headers: {
        'Content-Type': 'application/json',
      },
    };
    try {
      const res = await axios.post('/api/auth', formData, config);

      dispatch({
        type: LOGIN_SUCCESS,
        payload: res.data,
      });

      loadUser();
    } catch (err) {
      dispatch({
        type: LOGIN_FAIL,
        payload: err.response.data.msg,
      });
    }
  };
  //Logout
  const logout = () => {
    dispatch({ type: LOGOUT });
  };
  //Clear Errors
  const clearErrors = () => {
    dispatch({ type: CLEAR_ERRORS });
  };

  return (
    <AuthContext.Provider
      value={{
        token: state.token,
        isAuthenticated: state.isAuthenticated,
        loading: state.loading,
        user: state.user,
        error: state.error,
        loadUser,
        login,
        logout,
        clearErrors,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export default AuthState;

【问题讨论】:

  • state: typeof initialState, 这行看起来不太对,不应该是state: typeof AuthState,吗?
  • 嗨,汤姆,感谢您的回复。我试了一下,又玩了一点,但不幸的是,它似乎对给定的错误没有影响
  • 我看不到其他看起来很奇怪的东西 -> 你能告诉我USER_LOADED 是来自../types 文件夹吗

标签: javascript reactjs typescript use-context use-reducer


【解决方案1】:

问题出在这里:

const AuthReducer = (
  state: typeof initialState,
  action: Actions
): AuthState ...

typescript 无法关联您的类型 typeof initialStateAuthState。但是useReducer 的重载依赖于reducer 的类型来推断正确的结果类型。

这里有几个重构选项。你可以告诉 typescript 这些是相同的类型:

const AuthReducer = (
  state: AuthState,
  action: Actions
): AuthState

或者让你的initialState 更轻松一点:

const initialState: AuthState = {....}

问题的核心是TypeofAA的类型:

const a = { a: null }
type TypeofA = typeof a

type A = { a: string | null }

并不完全相同。简单地将字段声明为null 时,您将失去| string。为了使它们相等,您可以使用类型断言编写 a 对象:

const a = { a: null as string | null }

或者直接说明a的类型:

const a: A = { a: null }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-29
    • 2021-08-22
    • 1970-01-01
    • 2018-11-18
    • 2021-03-13
    • 1970-01-01
    • 1970-01-01
    • 2020-05-19
    相关资源
    最近更新 更多