简体   繁体   English

React-Redux中TypeScript中化简器的类型错误

[英]Error in Types for the reducers in TypeScript in React-Redux

I am using TypeScript for my React-Redux app. 我正在为我的React-Redux应用程序使用TypeScript。 I have followed the TypeScript React Starter , however, the example mentioned there does not have any action methods that has some arguments/payload. 我遵循TypeScript React Starter ,但是,上面提到的示例没有任何带有某些参数/有效载荷的操作方法。

I have a simple requirement - 我有一个简单的要求-

Show a list of names and when a button is clicked a new name is added. 显示名称列表,并在单击按钮时添加新名称。 When another button is clicked 2 names are added. 单击另一个按钮时,将添加2个名称。


The entry point for the app index.tsx looks like this - 应用index.tsx的入口点看起来像这样-

import * as React from 'react';
import * as ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import './index.css';
import { createStore } from 'redux';
import { StoreState } from './types';
import { listUsers } from './reducers';
import { Provider } from 'react-redux';
import ListUsers from './containers/ListUsers';

const store = createStore<StoreState>(listUsers, {
  'users': [
    {
      'id': 0,
      'firstName': 'Aniyah',
      'lastName': 'Luettgen',
      'phone': '861-332-5113',
      'email': 'Danika.Ryan84@yahoo.com',
      'role': 'admin'
    },
    {
      'id': 1,
      'firstName': 'Alisa',
      'lastName': 'Pacocha',
      'phone': '085-056-3901',
      'email': 'Eusebio68@yahoo.com',
      'role': 'admin'
    }
  ]
});

ReactDOM.render(
  <Provider store={store}>
    <ListUsers/>
  </Provider>,
  document.getElementById('root') as HTMLElement
);
registerServiceWorker();

The constants file ( constants/index.tsx ) looks like this - 常量文件( constants/index.tsx )看起来像这样-

export const LIST_USERS = 'LIST_USERS';
export type LIST_USERS = typeof LIST_USERS;

export const ADD_USER = 'ADD_USER';
export type ADD_USER = typeof ADD_USER;

export const ADD_USERS = 'ADD_USERS';
export type ADD_USERS = typeof ADD_USERS;

The types ( types/index.tsx ) are - 类型( types/index.tsx )是-

export interface User {
  id: number;
  firstName: string;
  lastName: string;
  phone: string;
  email: string;
  role: string;
}

export interface StoreState {
  users: Array<User>;
}

export interface Action<T> {
  type: string;
  payload?: T;
}

My Component ( components/ListUsers.tsx ) looks like this - 我的组件( components/ListUsers.tsx )看起来像这样-

import * as React from 'react';
import { DispatchProps, ListUsersProps } from '../containers/ListUsers';
import { User } from '../types';

interface ListState {
  counter: number;
}

class ListUsers extends React.Component<ListUsersProps & DispatchProps, ListState> {

  constructor(props: ListUsersProps & DispatchProps) {
    super(props);

    this.addUser = this.addUser.bind(this);
    this.addMultipleUser = this.addMultipleUser.bind(this);

    this.state = {
      counter: 0
    };
  }

  render() {
    return (
      <div>
        <button onClick={() => this.addUser()}>Add User</button>
        <button onClick={() => this.addMultipleUser()}>Add User</button>
        {this.props.users.map((user: User) => (
          <div key={user.id}>{user.firstName}</div>
        ))}
      </div>
    );
  }

  private addUser() {
    this.props.addUser({
      id: this.state.counter,
      firstName: 'abcd',
      lastName: 'efgh',
      email: 'abcd@gmail.com',
      phone: '1234567890',
      role: 'admin'
    });
    this.setState({
      ...this.state,
      counter: this.state.counter + 1
    });
  }

  private addMultipleUser() {
    this.props.addUsers([{
      id: this.state.counter,
      firstName: 'abcd',
      lastName: 'efgh',
      email: 'abcd@gmail.com',
      phone: '1234567890',
      role: 'admin'
    }, {
      id: this.state.counter,
      firstName: 'ijkl',
      lastName: 'mnop',
      email: 'ijkl@gmail.com',
      phone: '1234567890',
      role: 'admin'
    }]);
    this.setState({
      ...this.state,
      counter: this.state.counter + 2
    });
  }
}

export default ListUsers;

My container component ( containers/ListUsers.tsx ) looks like this - 我的容器组件( containers/ListUsers.tsx )看起来像这样-

import { connect, Dispatch } from 'react-redux';
import { Action, StoreState, User } from '../types';
import ListUsers from '../components/ListUsers';
import { addUser, addUsers } from '../actions';

export interface ListUsersProps {
  users: Array<User>;
}

export interface DispatchProps {
  addUser(user: User): Action<User>;
  addUsers(users: Array<User>): Action<Array<User>>;
}

export const mapStateToProps = (state: StoreState): ListUsersProps => {
  return {
    users: state.users
  };
};

export const mapDispatchToProps = (dispatch: Dispatch<DispatchProps>) => {
  return {
    addUser: (user: User) => dispatch(addUser(user)),
    addUsers: (users: Array<User>) => dispatch(addUsers(users))
  };
};

export default connect<ListUsersProps, DispatchProps, {}>(mapStateToProps, mapDispatchToProps)(ListUsers);

Now here is the reducer where I am facing issues ( reducers/index.tsx ) - 现在这是我面临问题的reducers/index.tsxreducers/index.tsx )-

import { Action, StoreState, User } from '../types';
import { ADD_USER, ADD_USERS, LIST_USERS } from '../constants';

export const listUsers = (state: StoreState, action: Action<User>): StoreState => {
  switch (action.type) {
    case LIST_USERS:
      return state;
    case ADD_USER:
      return {
        ...state,
        users: action.payload === undefined ? [...state.users] : [...state.users, action.payload]
      };
    case ADD_USERS:
      return {
        ...state,
        users: action.payload === undefined ? [...state.users] : [...state.users, ...action.payload]
      };
    default:
      return state;
  }
};

Here the compiler says that in the reducers/index.tsx in 这里的编译器说在reducers/index.tsx

case ADD_USERS:
  return {
    ...state,
    users: action.payload === undefined ? [...state.users] : [...state.users, ...action.payload]
  };

that action.payload is User and not Array<User> which is right (I have declared the reducer with type (state: StoreState, action: Action<User>) ). action.payloadUser而不是Array<User> ,这是正确的(我已将reducer声明为类型(state: StoreState, action: Action<User>) )。

How do I work with this? 我该如何处理? If there isn't an alternative, I will have to write separate reducers for every ACTION that has a different payload which I think is bad. 如果没有其他选择,我将不得不为每个具有不同payload ACTION编写单独的reducer,我认为这是不好的。

The way I am using the DispatchProps type also looks very odd to me. 我使用DispatchProps类型的方式在我看来也很奇怪。 I am trying to follow this for the types of mapStateToProps and mapDispatchToProps . 我试图按照为各类mapStateToPropsmapDispatchToProps

Is there any work around for this? 有什么解决办法吗?

PS: I am using react , redux and react-redux libraries. PS:我正在使用reactreduxreact-redux库。

I found a solution for this. 我找到了解决方案。 I am not sure if it is right way to do this. 我不确定这样做是否正确。


Instead of making the Action Type generic like so - 与其像这样使动作类型通用,不如说是-

interface Action<T> { 
  type: string;
  payload?: T;
}

I created a Separate Action Type for each Action and then the Action Type accepted by the Reducer would be the | 我为每个Action创建了一个单独的动作类型 ,然后减速器接受的动作类型将为| of them. 其中。


So, the types look like this ( types/index.tsx ) - 因此,类型看起来像这样( types/index.tsx )-

import { ADD_USER, ADD_USERS, LIST_USERS } from '../constants';

export interface User {
  id: number;
  firstName: string;
  lastName: string;
  phone: string;
  email: string;
  role: string;
}

export interface StoreState {
  users: Array<User>;
}

export type ListUsersAction = {type: typeof LIST_USERS};
export type AddUserAction = {type: typeof ADD_USER, payload: User};
export type AddUsersAction = {type: typeof ADD_USERS, payload: Array<User>};

export type Actions = ListUsersAction | AddUserAction | AddUsersAction;

and the Container Component ( containers/ListUsers.tsx ) looks like this - 容器组件( containers/ListUsers.tsx )看起来像这样-

import { connect, Dispatch } from 'react-redux';
import { AddUserAction, AddUsersAction, StoreState, User } from '../types';
import ListUsers from '../components/ListUsers';
import { addUser, addUsers } from '../actions';

export interface ListUsersProps {
  users: Array<User>;
}

export interface DispatchProps {
  addUser(user: User): AddUserAction;
  addUsers(users: Array<User>): AddUsersAction;
}

export const mapStateToProps = (state: StoreState): ListUsersProps => {
  return {
    users: state.users
  };
};

export const mapDispatchToProps = (dispatch: Dispatch<DispatchProps>) => {
  return {
    addUser: (user: User) => dispatch(addUser(user)),
    addUsers: (users: Array<User>) => dispatch(addUsers(users))
  };
};

export default connect<ListUsersProps, DispatchProps>(mapStateToProps, mapDispatchToProps)(ListUsers);

There are no compilation errors any more, and payload for both ADD_USER and ADD_USERS is received properly. 不再有编译错误,并且已正确接收ADD_USERADD_USERS payload


There is a recommendation to reduce the Action Type boilerplate here . 还有就是要减少操作类型样板推荐这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM