javascript - Redux + TypeScript 和 mapDispatchToProps

标签 javascript reactjs typescript redux react-redux

这应该很容易,但目前我无法理解,但我觉得这应该是一个简单的修复。我目前正在将 reduxtypescript 结合使用,并使用 redux-thunk 来创建异步操作。

设置很简单。这是我用于登录的代码:

export function requestAuthenticationAsync(email: string, password: string) {
    return (dispatch: ThunkDispatch<IState, undefined, IAction>): Promise<void> => {
        dispatch(requestAuthentication());

        return postAuthentication(email, password).then((response) => {
            dispatch(receiveAuthentication());

            return response.json();
        }).then((data) => {
            dispatch(receiveUser(data));
        });
    };
}

理想的情况是,当登录成功时,我可以使用 .then.tsx 文件中调用它,以导航到其他地方。

因此,当我在组件中执行类似操作时,这将按照您的预期工作:

const { dispatch } = store;

dispatch(requestAuthenticationAsync('email', 'password')).then(() => {
    // navigate somewhere
});

但是,当我使用 react-redux 中的 connectmapDispatchToProps 时,如下所示:

import './Gateway.scss';
import * as React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { requestAuthenticationAsync } from './actions';
import { withRouter } from 'react-router-dom';

const mapDispatchToProps = (dispatch) => {
    return {
        requestAuthenticationAsync: bindActionCreators(requestAuthenicationAsync, dispatch)
    };
};

const mapStateToProps = (state) => {
    return {
        authenticated: state.authentication.authenticated
    };
};

class Gateway extends React.Component<{
    authenticated: boolean;
    requestAuthenticationAsync: typeof requestAuthenticationAsync;
}, {
    email: string;
    password: string;
}> {

    constructor(props) {
        super(props);

        this.state = {
            email: '',
            password: ''
        };
    }

    onGatewaySubmit = (event) => {
        event.preventDefault();

        const { requestAuthenticationAsync } = this.props;
        const { email, password } = this.state;

        requestAuthenticationAsync(email, password).then(() => {
            console.log('done');
        });
    };

    onEmailValueChange = (event) => {

        this.setState({
            email: event.target.value
        });
    };

    onPasswordValueChange = (event) => {
        this.setState({
            password: event.target.value
        });
    };

    render() {
        return (
            <div id='gateway'>
                <form onSubmit={ this.onGatewaySubmit }>
                    <input
                        className='email'
                        onChange={ this.onEmailValueChange }
                        placeholder='email'
                        type='text' />
                    <input
                        className='password'
                        onChange={ this.onPasswordValueChange }
                        placeholder='password'
                        type='password' />
                    <input type='submit' value='Submit' />
                </form>
            </div>
        );
    }
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Gateway));

我收到以下错误:

TS2339: Property 'then' does not exist on type '(dispatch: ThunkDispatch<IState, undefined, IAction>) => Promise<void>'.

什么给了?在这种情况下,如何让 TypeScript 满意,以便我可以使用带有 .then 的 Promise?

最佳答案

问题的根本原因是 redux-thunk是由 redux 执行的中间件所以它调用函数(thunk)并返回值。但是,TypeScript 无法“意识到”正在发生的情况,因此无法正确键入它(无需进行一些额外的工作)。

redux-thunk包(此时)实际上附带了类型定义。然而,对其类型定义进行了许多重大改进,但没有发布。 It sounds like在 3.0 版本中,它们将被删除并移至 DefinelyTyped(可通过 @types/redux-thunk 安装)。

但在那之前,您可以自己设置类型。如果你compare what is released todaywhat is in the repo类型定义相对较多。

要使用它们(在新版本 redux-thunk 或 DefinelyTyped 中发布之前),您可以使用以下内容创建一个类型文件(例如: types.d.ts ):

import { ActionCreatorsMapObject } from "redux";
import { ThunkAction } from "redux-thunk";

/**
 * Redux behaviour changed by middleware, so overloads here
 */
declare module "redux" {
  /**
   * Overload for bindActionCreators redux function, returns expects responses
   * from thunk actions
   */
  function bindActionCreators<
    TActionCreators extends ActionCreatorsMapObject<any>
  >(
    actionCreators: TActionCreators,
    dispatch: Dispatch
  ): {
    [TActionCreatorName in keyof TActionCreators]: ReturnType<
      TActionCreators[TActionCreatorName]
    > extends ThunkAction<any, any, any, any>
      ? (
          ...args: Parameters<TActionCreators[TActionCreatorName]>
        ) => ReturnType<ReturnType<TActionCreators[TActionCreatorName]>>
      : TActionCreators[TActionCreatorName]
  };
}

这是直接从今天的存储库中提取的。如果您需要更多,您可以复制整个文件,但这一部分应该可以解决您的问题。

然后,将您的通话更新为 bindActionCreators传递一个对象并推断这些类型(这对于 mapStateToProps 来说并不是绝对必要的,但我发现避免“双重”输入更容易一些):

type DispatchProps = ReturnType<typeof mapDispatchToProps>;
const mapDispatchToProps = dispatch => {
  return bindActionCreators({ requestAuthenticationAsync }, dispatch);
};

type StateProps = ReturnType<typeof mapStateToProps>;
const mapStateToProps = state => ({
  authenticated: state
});

type Props = DispatchProps & StateProps;

class Gateway extends React.Component<Props> {
  // ...
}

可以更新类型,但使用 redux-thunk 中的类型今天 repo ,他们期望第一个参数为 bindActionCreators通过查看 TActionCreators extends ActionCreatorsMapObject<any> 成为一个对象(尽管 the docs say it can be either a function as you were using or an object ) .

现在应该正确输入 this.props.requestAuthenticationAsync用于您的组件。

onGatewaySubmit = event => {
  event.preventDefault();

  const { requestAuthenticationAsync } = this.props;
  const { email, password } = this.state;

  // Type:
  //   (email: string, password: string) => Promise<void>
  requestAuthenticationAsync(email, password).then(() => {
    console.log("done");
  });
};

关于javascript - Redux + TypeScript 和 mapDispatchToProps,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58385940/

相关文章:

javascript - Passportjs 如何返回验证结果而不是重定向到另一个页面

javascript - 从 CodeIgniter 设置 cookie 并使用 javascript 获取值

javascript - 如何使用 reactjs 和 material ui 将 textField 添加到表中的特定行 onclick of a button

jquery - 使用 PrimeNG 日历检测 [(ngmodel) 对所选日期的更改

javascript - 粘贴文本时基于字数限制文本输入的 jQuery 函数不起作用

javascript - 从 Wordpress 站点导出已编译的 CSS 和 JS

javascript - 如何链接到 React Router 中的嵌套路由

javascript - 使用两个函数更新状态,无法正确更新组件

javascript - 收到新数据时如何更新 d3 图表

reactjs - NavLink isActive 的正确接口(interface)是什么?