简体   繁体   English

如何在带有 Typescript 的 react-redux 中正确使用“connect”

[英]How to correctly use 'connect' in react-redux with Typescript

I'm using typescript in an application, but found some problem with react-redux.我在应用程序中使用打字稿,但发现 react-redux 存在一些问题。 The 'connect' method reports a problem and I have no idea with it since I'm a rookie with typescript and redux. 'connect' 方法报告了一个问题,我不知道它,因为我是一个打字稿和 redux 的新手。 What should I do or where in my code should be modified?我应该怎么做或应该修改代码中的哪些地方? Thanks a lot非常感谢

Application built with typescript@3.3 , react@16.8.5 , react-redux@7.1.0 .应用内置typescript@3.3react@16.8.5react-redux@7.1.0

// article
interface IArticle {
  title: string,
  author: string
}

// state
interface MyState {
  list: [],
  article: IArticle
}

// stateMapToProps
interface MyStateProps {
  article: IArticle
}

// router match
interface MatchParams {
  id: string
}

// own props
interface MyOwnProps extends RouteComponentProps < MatchParams > {
  article: IArticle,
  dispatch: (o: object) => {}
}

class ArticleContainer extends Component < MyOwnProps, {} > {
  constructor(props: MyOwnProps) {
    super(props);
  }

  componentDidMount() {
    const {
      dispatch
    } = this.props;
    const id = this.props.match.params.id
    dispatch(fetchArticle(id))
  }

  render() {
    const {
      article
    } = this.props;
    return ( <
      Article article = {
        article
      } > < /Article>
    )
  }
}

const mapStateToProps = (state: MyState): MyStateProps => {
  return {
    article: state.article
  }
}

export default connect < MyStateProps, {}, {
  article: IArticle
} > (
  mapStateToProps
)(ArticleContainer)

Here is the code of async action fetchArticle这是异步操作fetchArticle的代码

function fetchArticle(id: string) {
  return function(dispatch: (action: AnyAction) => {}): Promise<void> {
    dispatch(getArticle(id))

    return axios.get(`/article/${id}`)
      .then(res => {
        dispatch(getArticleSuccess(res.data))
      })
  }
}

Error happens at the export line and message is as below: export行发生错误,消息如下:

Argument of type '(state: MyState) => MyStateProps' is not assignable to parameter of type 'MapStateToPropsParam'. “(state: MyState) => MyStateProps”类型的参数不可分配给“MapStateToPropsParam”类型的参数。 Type '(state: MyState) => MyStateProps' is not assignable to type 'MapStateToPropsFactory'.类型 '(state: MyState) => MyStateProps' 不可分配给类型 'MapStateToPropsFactory'。 Types of parameters 'state' and 'initialState' are incompatible.参数“state”和“initialState”的类型不兼容。 Type '{}' is missing the following properties from type 'MyState': list, articlets(2345)类型“{}”缺少类型“MyState”中的以下属性:list、articlets(2345)

Minimum steps to be able to compile your code:能够编译代码的最低步骤:

  1. MyOwnProps should be MyOwnProps应该是

    import { AnyAction } from 'redux'; interface AppThunkAction<TAction> { (dispatch: (action: TAction) => void, getState: () => MyState): any; } // As you're going to dispatch thunk actions, dispatch should be overloaded interface Dispatch<TAction> { (action: AppThunkAction<TAction>): any (action: TAction): TAction } // own props interface MyOwnProps extends RouteComponentProps<MatchParams> { article: IArticle, dispatch: Dispatch<AnyAction> }
  2. If you want to provide types for connect function, add MyState as last type like so如果要为connect函数提供类型,请将MyState添加为最后一个类型,如下所示

    export default connect <MyStateProps, {}, { article: IArticle }, MyState >( mapStateToProps )(ArticleContainer)

    Or you can allow compiler to infer types itself, which is preferred或者您可以允许编译器自行推断类型,这是首选

    export default connect( mapStateToProps )(ArticleContainer)

So working result所以工作结果

import { Component } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { connect, ResolveThunks } from 'react-redux';
import { AnyAction } from 'redux';
import axios from 'axios';

// article
interface IArticle {
    title: string,
    author: string
}

// state
interface MyState {
    list: [],
    article: IArticle
}

// stateMapToProps
interface MyStateProps {
    article: IArticle
}

// router match
interface MatchParams {
    id: string
}

export interface AppThunkAction<TAction> {
    (dispatch: (action: TAction) => void, getState: () => MyState): any;
}

interface Dispatch<TAction> {
    (action: AppThunkAction<TAction>): any
    (action: TAction): TAction
}

// own props
interface MyOwnProps extends RouteComponentProps<MatchParams> {
    article: IArticle,
    dispatch: Dispatch<AnyAction>
}

function getArticle(id: string) {
    return {
        type: 'GET_ARTICLE',
        id
    }
}

function getArticleSuccess(i: any) {
    return {
        type: 'SET_ARTICLE',
        i
    }
}



const fetchArticle = (id: string): AppThunkAction<AnyAction> =>
    (dispatch, getState) => {
        dispatch(getArticle(id))

        return axios.get(`/article/${id}`)
            .then(res => {
                dispatch(getArticleSuccess(res.data))
            })
    }

class ArticleContainer extends Component<MyOwnProps, {}> {
    constructor(props: MyOwnProps) {
        super(props);
    }

    componentDidMount() {
        const {
            dispatch
        } = this.props;
        const id = this.props.match.params.id
        dispatch(fetchArticle(id))
    }

    render() {
        const {
            article
        } = this.props;
        return (<div>article: {article}</div>
          )
        }
      }

const mapStateToProps = (state: MyState): MyStateProps => {
  return {
                article: state.article
          }
        }

export default connect(
            mapStateToProps
)(ArticleContainer)

Here is a working example, how to type a react file when using redux, based on your example.这是一个工作示例,根据您的示例,如何在使用 redux 时键入 react 文件。

// article-container.js file
import { connect, DispatchProp } from "react-redux";
import { Component } from "react";

// import the real RouteComponent, this is just a demo example
interface RouteComponentProps<T> {
  match: { params: T };
}
// article
interface IArticle {
  title: string;
  author: string;
}
// import the real Article, this is just a demo example

const Article = ({ article }: { article: IArticle }) => {
  return <div>{article.title}</div>;
};

// import the real fetchArticle, this is just a demo example

const fetchArticle = (id: string) => {
  return {
    type: "SOME_ACTION",
    payload: {
      id,
    },
  };
};

// state
interface MyState {
  list: [];
  article: IArticle;
}

// stateMapToProps
interface MyStateProps {
  article: IArticle;
}

// router match
interface MatchParams {
  id: string;
}

// own props
interface MyOwnProps {
  article: IArticle;
}
type AllProps = MyOwnProps & RouteComponentProps<MatchParams> & DispatchProp;

class ArticleContainer extends Component<AllProps> {
  constructor(props: AllProps) {
    super(props);
  }

  componentDidMount() {
    const { dispatch } = this.props;
    const id = this.props.match.params.id;
    dispatch(fetchArticle(id));
  }

  render() {
    const { article } = this.props;
    return <Article article={article} />;
  }
}

const mapStateToProps = (state: MyState): MyStateProps => {
  return {
    article: state.article,
  };
};

export default connect(
  mapStateToProps
)(ArticleContainer);

And use it like so并像这样使用它


import ArticleContainer from "./article-container";

export default () => {
  // this is coming from router, just an example for demonstration
  const match = { params: { id: "23" } };
  return (
    <div>
      <ArticleContainer match={match} />
    </div>
  );
};

Finally solved with remove the generics declaration of connect method and use ThunkDispatch with async action creator.最后通过删除connect方法的泛型声明并使用ThunkDispatch与异步操作创建者解决。 The code is below.代码如下。

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { ThunkDispatch } from 'redux-thunk';
import { fetchArticle } from '../store/actions';
import { RouteComponentProps } from 'react-router-dom';
import Article from '../components/Article/Article'
import { AnyAction } from 'redux';

// article
interface IArticle {
  title: string,
  author: string
}

// state
interface MyState {
  list: [],
  article: IArticle
}

// stateMapToProps
interface StateToProps {
  article: IArticle
}

// router match
interface MatchParams {
  id: string
}

// own props
interface MyOwnProps extends RouteComponentProps<MatchParams> {
  article: IArticle,
  getArticle: (id: string) => Promise<void>
}

class ArticleContainer extends Component<MyOwnProps, {}> {
  constructor(props: MyOwnProps) {
    super(props);
  }

  componentDidMount() {
    const { getArticle } = this.props;
    const id = this.props.match.params.id
    getArticle(id)
  }

  render() {
    const { article } = this.props;
    return (
      <Article article={article}></Article>
    )
  }
}

const mapStateToProps = (state: MyState): StateToProps => {
  return {
    article: state.article
  }
}

const mapDispatchToProps = (dispatch: ThunkDispatch<any, any, AnyAction>) => {
  return {
    getArticle: (id: string) => dispatch(fetchArticle(id))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ArticleContainer)

Thanks for the help!谢谢您的帮助!

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

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