【问题标题】:mapDispatchToProps type challenge (typescript react-redux)mapDispatchToProps 类型挑战(typescript react-redux)
【发布时间】:2021-02-01 17:18:07
【问题描述】:

我无法确定我的 mapDispatchToProps 的类型。检查下面的 SignInComponent const mapDispatchToProps。 看起来很简单,因为函数将 signIn() 作为参数,我可以从中获得类型。但是我无法弄清楚。

这是我的 autAction.ts:

import firebase from 'firebase/app'
import { Dispatch } from 'react';

type AuthAction = {
  type: string,
  err?: unknown
}

export const signIn = (credentials: {email:string, password:string}) => {
  return (dispatch: Dispatch<AuthAction>) => {
    firebase.auth().signInWithEmailAndPassword(
      credentials.email,
      credentials.password
    ).then(() => {
      dispatch({ type: 'LOGIN_SUCCESS'})
    }).catch((err) => {
      dispatch({ type: 'LOGIN_ERROR', err})
    });
  }
}

这是我的登录组件:

import React, { BaseSyntheticEvent, Component } from 'react'
import { connect } from 'react-redux';
import { signIn } from '../../store/actions/authActions';


type IConnectedDispatch = {
  signIn: typeof signIn
}

interface IMyComponentProps {
  signIn: (credentials: {email:string, password:string}) => void;
}

class SignIn extends Component<IMyComponentProps> {
  state = {
    email:'',
    password:''
  }

  handleChange = (e:BaseSyntheticEvent) => {
    this.setState({
      [e.target.id]: e.target.value
    });
  }

  handleSubmit = (e:BaseSyntheticEvent) => {
    e.preventDefault();
    //console.log(this.state);
    this.props.signIn(this.state);
  }

  render() {
    return (
      <div className="container">
        <form onSubmit={this.handleSubmit} className="white">
          <h5 className="grey-text text-darken-3">Sing In</h5>
          <div className="input-field">
            <label htmlFor="email">Email</label>
            <input type="email" id="email" onChange={this.handleChange}/>
          </div>
          <div className="input-field">
            <label htmlFor="password">Password</label>
            <input type="password" id="password" onChange={this.handleChange}/>
          </div>
          <div className="input-field">
            <button className="btn pink lighten-1 z-depth-0">Login</button>
          </div>
        </form> 
      </div>
    )
  }
}

type AuthAction = {
  type:string,
  err?: unknown
}

const mapDispatchToProps = (dispatch: any): IConnectedDispatch  => { //TYPE CHALLENGE HERE. 
  return {
    signIn: (credentials:{ email:string, password:string}) => dispatch(signIn(credentials))
  }
}

export default connect<React.FunctionComponent>(null,mapDispatchToProps)(SignIn)

【问题讨论】:

    标签: reactjs typescript react-redux mapdispatchtoprops


    【解决方案1】:

    如@liamgbs 的回答所示,您的mapDispatchToProps 并不是真正必要的,但我确实想回答您有关打字稿问题的问题。

    mapDispatchToProps 是一个以dispatch 为参数并返回props 的键控对象的函数。所以类型本质上是(dispatch: Dispatch)

    问题是哪个在这里使用Dispatch 定义,因为多个包都有自己的类型。来自reactreduxDispatch 的更基本定义预计dispatch 将仅使用普通操作对象调用。 signIn 动作创建者的返回值是 dispatch 的函数(“thunk”)。所以你需要使用 redux-thunk 包中的类型。

    {signIn: typeof signIn} 也不完全正确,因为您调用 signIn 而不仅仅是返回它。实际接口是您的IMyComponentProps,其中signInvoid 函数。

    import { ThunkDispatch } from "redux-thunk";
    
    interface LoginCredentials {
      email: string;
      password: string;
    }
    
    interface IMyComponentProps {
      signIn: (credentials: LoginCredentials) => void;
    }
    
    const mapDispatchToProps = ( dispatch: ThunkDispatch<any, never, AuthAction> ): IMyComponentProps => {
      return {
        signIn: (credentials: LoginCredentials) => dispatch(signIn(credentials))
      };
    };
    

    令人讨厌的是,ThunkDispatch 要求您设置所有三个泛型。第一个 any 代表你的 redux 状态的类型,所以如果你愿意,你可以用实际的类型替换它。 never 是您未使用的动作创建器中的额外参数。

    【讨论】:

    • 非常好的答案,这就是我正在寻找的解释。非常感谢
    【解决方案2】:

    试试这样:

    import React, { BaseSyntheticEvent, Component } from 'react'
    import { connect } from 'react-redux';
    import { signIn } from '../../store/actions/authActions';
    
    interface IMyComponentProps {
      signIn: typeof signIn
    }
    
    class SignIn extends Component<IMyComponentProps> {
      state = {
        email:'',
        password:''
      }
    
      handleChange = (e:BaseSyntheticEvent) => {
        this.setState({
          [e.target.id]: e.target.value
        });
      }
    
      handleSubmit = (e:BaseSyntheticEvent) => {
        e.preventDefault();
        //console.log(this.state);
        this.props.signIn(this.state);
      }
    
      render() {
        return (
          <div className="container">
            <form onSubmit={this.handleSubmit} className="white">
              <h5 className="grey-text text-darken-3">Sing In</h5>
              <div className="input-field">
                <label htmlFor="email">Email</label>
                <input type="email" id="email" onChange={this.handleChange}/>
              </div>
              <div className="input-field">
                <label htmlFor="password">Password</label>
                <input type="password" id="password" onChange={this.handleChange}/>
              </div>
              <div className="input-field">
                <button className="btn pink lighten-1 z-depth-0">Login</button>
              </div>
            </form> 
          </div>
        )
      }
    }
    
    export default connect(null, { signIn } )(SignIn)
    

    【讨论】:

    • 非常感谢您的回答。我知道我可以做到这一点,const mapDispatchToProps = { signIn }; 但我想弄清楚我们的调度类型,我缺少什么,因为dispatch 正在接受参数的类型很清楚......