在 .NET Core 2.0 reactredux 模板项目中使用 react redux 和 typescript 将组件与 OwnProps 连接起来

Posted

技术标签:

【中文标题】在 .NET Core 2.0 reactredux 模板项目中使用 react redux 和 typescript 将组件与 OwnProps 连接起来【英文标题】:Connecting a component with OwnProps using react redux and typescript in the .NET Core 2.0 reactredux template project 【发布时间】:2018-05-18 10:08:38 【问题描述】:

源代码可以在这里找到:https://github.com/cvanem/ASPNETCoreReact16Redux

编辑:我能够使用以下代码使其 99% 正常工作。一切都在编译时运行,但在父组件中使用时,它会警告说属性计数丢失。实际执行时,一切都按预期工作。此计数属性位于 CounterStore 中,并在按下按钮时正确递增。一切正常,但我不知道如何摆脱打字稿警告。我在某处做错类型定义了吗?原始模板将 typeof Counter 添加到连接语句的末尾,如下所示:

    export default connect(mapStateToProps, mapStateToDispatch )(Counter) as typeof Counter;

当我用我的代码尝试上述操作时,它给出了以下错误:

    ERROR in [at-loader] ./ClientApp/components/Counter.tsx:39:16 
        TS2352: Type 'ComponentClass<Pick<CounterState & ComponentProps &  increment: () => IncrementCountAction; decr...' cannot be converted to type 'typeof Counter'.
    ERROR in [at-loader] ./ClientApp/components/Counter.tsx:39:16 
        TS2352: Type 'ComponentClass<Pick<CounterState & ComponentProps &  increment: () => IncrementCountAction; decr...' cannot be converted to type 'typeof Counter'.
        Type 'Component<Pick<CounterState & ComponentProps &  increment: () => IncrementCountAction; decrement...' is not comparable to type 'Counter'.
Types of property 'setState' are incompatible.
          Type ' <K extends never>(f: (prevState: Readonly<ComponentState>, props: Pick<CounterState & Component...' is not comparable to type ' <K extends never>(f: (prevState: Readonly<>, props: CounterState & ComponentProps &  increme...'.
            Types of parameters 'f' and 'f' are incompatible.
              Types of parameters 'props' and 'props' are incompatible.
                Type 'Pick<CounterState & ComponentProps &  increment: () => IncrementCountAction; decrement: () => De...' is not comparable to type 'CounterState & ComponentProps &  increment: () => IncrementCountAction; decrement: () => Decreme...'.
                  Type 'Pick<CounterState & ComponentProps &  increment: () => IncrementCountAction; decrement: () => De...' is not comparable to type 'CounterState'.
                    Property 'count' is missing in type 'Pick<CounterState & ComponentProps &  increment: () => IncrementCountAction; decrement: () => De...'.
     ERROR in [at-loader] ./ClientApp/components/Home.tsx:16:13 
        TS2322: Type ' test: "hello"; children: never[]; ' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Counter> & Readonly< children?: ReactNode; > & R...'.
          Type ' test: "hello"; children: never[]; ' is not assignable to type 'Readonly<CounterState & ComponentProps &  increment: () => IncrementCountAction; decrement: () =...'.
            Property 'count' is missing in type ' test: "hello"; children: never[]; '.

这是我所拥有的,它可以编译并工作,但仍然给出设计时警告:

    export default connect(mapStateToProps, mapStateToDispatch )(Counter);

有什么想法吗?这是包含所有内容的完整组件代码,但当 Counter 组件被父组件(即)使用时,仍会在设计时发出打字稿警告:

    import * as React from 'react';
    import  Link, RouteComponentProps  from 'react-router-dom';
    import  connect  from 'react-redux';
    import  ApplicationState   from '../store';
    import * as CounterStore from '../store/Counter';        

    type ComponentStateProps = CounterStore.CounterState & ComponentProps; //type definition for component props merged with state props (for typings on the connect function)
    type CounterProps = ComponentStateProps & typeof CounterStore.actionCreators; 
    //type definition for the component class

    interface ComponentProps 
        test: string;
    

    class Counter extends React.Component<CounterProps, > 
        public render() 
            return <div>
                <h1>Counter</h1>

                <p>This is a simple example of a React component.</p>

                <p>Current count: <strong> this.props.count </strong></p>
        <button onClick=() =>  this.props.increment() 
    >Increment</button>      
                <p>Component Prop = this.props.test</p>
            </div>;
        
    

    const mapStateToProps = (state: ApplicationState, ownProp: ComponentProps): 
    ComponentStateProps => 
        return (
            
                ...state.counter,
                test: ownProp.test      
            
        );
    ;

    const mapStateToDispatch = CounterStore.actionCreators;

    export default connect(mapStateToProps, mapStateToDispatch )(Counter);

原帖:

我从 Visual Studio 2017 模板创建了一个新的 react redux 项目,方法是在命令提示符下运行:

dotnet new reactredux

我正在尝试将一个组件连接到 redux 商店。组件有它自己的属性,ComponentProps。当我尝试使用它自己的属性将它连接到商店时,我收到以下错误:

./ClientApp/components/Counter.tsx:34:5 

TS2345: Argument of type '(state: ApplicationState) => CounterState' is not assignable to parameter of type 'MapStateToPropsParam<, ComponentProps,>'.

Type '(state: ApplicationState) => CounterState' is not assignable to type 'MapStateToProps<, ComponentProps, >'.

Types of parameters 'state' and 'state' are incompatible.
Type '' is not assignable to type 'ApplicationState'.
Property 'counter' is missing in type ''.

我添加了 ComponentProps 接口,将其包含在 CounterProps 中,并将其添加到底部的连接调用中。这是组件代码:

    import * as React from 'react';
    import  Link, RouteComponentProps  from 'react-router-dom';
    import  connect  from 'react-redux';
    import  ApplicationState   from '../store';
    import * as CounterStore from '../store/Counter';
    import * as WeatherForecasts from '../store/WeatherForecasts';

    interface ComponentProps 
        test: string;
    

    type CounterProps =
    CounterStore.CounterState
        & typeof CounterStore.actionCreators
        & RouteComponentProps<>
        & ComponentProps;   

    class Counter extends React.Component<CounterProps, > 
        public render() 
            return <div>
                <h1>Counter</h1>

                <p>This is a simple example of a React component.</p>

                <p>Current count: <strong> this.props.count </strong></p>

                <button onClick= () =>  this.props.increment()  
    >Increment</button>
            </div>;
        
    

    // Wire up the React component to the Redux store
    export default connect<, , ComponentProps>(
        (state: ApplicationState) => state.counter, // Selects which state properties are merged into the component's props
        CounterStore.actionCreators, // Selects which action creators are merged into the component's props
    )(Counter) as typeof Counter;

有人可以告诉我使用 connect 将具有自己属性的组件连接到商店的正确方法吗?我尝试的一切似乎都会产生错误。 @types/react-redux 文件有一个关于传递第三个参数的注释,并说用户必须扩展 ownProps 接口,但我不确定如何做到这一点。这是 index.d.ts 文件中@types/react-redux 的部分:

    /**
     * Connects a React component to a Redux store.
     *
     * - Without arguments, just wraps the component, without changing the behavior / props
     *
     * - If 2 params are passed (3rd param, mergeProps, is skipped), default behavior
     * is to override ownProps (as stated in the docs), so what remains is everything that's
     * not a state or dispatch prop
     *
     * - When 3rd param is passed, we don't know if ownProps propagate and whether they
     * should be valid component props, because it depends on mergeProps implementation.
     * As such, it is the user's responsibility to extend ownProps interface from state or
     * dispatch props or both when applicable
     *
     * @param mapStateToProps
     * @param mapDispatchToProps
     * @param mergeProps
     * @param options
     */
    export interface Connect 
        (): InferableComponentEnhancer<DispatchProp<any>>;

        <TStateProps = , no_dispatch = , TOwnProps = , State = >(
            mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, 
            State>): InferableComponentEnhancerWithProps<TStateProps & 
            DispatchProp<any>, TOwnProps>;

        <no_state = , TDispatchProps = , TOwnProps = >(
            mapStateToProps: null | undefined,
            mapDispatchToProps: MapDispatchToPropsParam<TDispatchProps, 
            TOwnProps>
            ): InferableComponentEnhancerWithProps<TDispatchProps, 
            TOwnProps>;

        <TStateProps = , TDispatchProps = , TOwnProps = , State = >(
            mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, 
            State>,: MapDispatchToPropsParam<TDispatchProps, TOwnProps>
            ): InferableComponentEnhancerWithProps<TStateProps & 
            TDispatchProps, TOwnProps>;

        <TStateProps = , no_dispatch = , TOwnProps = , TMergedProps = 
            , State = >(: MapStateToPropsParam<TStateProps, TOwnProps, 
            State>,: null | undefined,: MergeProps<TStateProps, undefined, 
            TOwnProps, >,<TMergedProps, TOwnProps>;

        <no_state = , TDispatchProps = , TOwnProps = , TMergedProps = 
            >(: null | undefined,: MapDispatchToPropsParam<TDispatchProps, 
            TOwnProps>,: MergeProps<undefined, TDispatchProps, TOwnProps, 
            TMergedProps>,<TMergedProps, TOwnProps>;

        <no_state = , no_dispatch = , TOwnProps = , TMergedProps = >
            (: null | undefined,: null | undefined,: MergeProps<undefined, 
            undefined, TOwnProps, TMergedProps>,<TMergedProps, TOwnProps>;

        <TStateProps = , TDispatchProps = , TOwnProps = , TMergedProps 
             = , State = >(: MapStateToPropsParam<TStateProps, 
             TOwnProps, State>,: MapDispatchToPropsParam<TDispatchProps, 
             TOwnProps>,: MergeProps<TStateProps, TDispatchProps, TOwnProps, 
             TMergedProps>,<TMergedProps, TOwnProps>;

        <TStateProps = , no_dispatch = , TOwnProps = , State = >(
             mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, 
             State>,: null | undefined,: null | undefined,: Options<State, 
             TStateProps, TOwnProps>): 
             InferableComponentEnhancerWithProps<DispatchProp<any> & 
             TStateProps, TOwnProps>;

        <TStateProps = , TDispatchProps = , TOwnProps = >(
             mapStateToProps: null | undefined,: 
             MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
             mergeProps: null | undefined,: Options<, TStateProps, 
             TOwnProps><TDispatchProps, TOwnProps>;

        <TStateProps = , TDispatchProps = , TOwnProps = , State = >(
             mapStateToProps: MapStateToPropsParam<TStateProps, TOwnProps, 
             State>,: MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
             mergeProps: null | undefined,: Options<State, TStateProps, 
             TOwnProps>): InferableComponentEnhancerWithProps<TStateProps & 
             TDispatchProps, TOwnProps>;

        <TStateProps = , TDispatchProps = , TOwnProps = , TMergedProps 
             = , State = >(mapStateToProps: 
             MapStateToPropsParam<TStateProps, TOwnProps, 
             State>,mapDispatchToProps: 
             MapDispatchToPropsParam<TDispatchProps, TOwnProps>,
             mergeProps: MergeProps<TStateProps, TDispatchProps, TOwnProps, 
             TMergedProps>,
             options: Options<State, TStateProps, TOwnProps, TMergedProps>
             ): InferableComponentEnhancerWithProps<TMergedProps, 
             TOwnProps>;

【问题讨论】:

嘿,你解决这个问题了吗?我有同样的问题。一切都很好,直到我尝试在另一个组件中使用组件。就像我需要传递 @connect 应该在内部提供的所有道具。除非我有什么问题。 【参考方案1】:

使用一些Domain:

    export type DomainState = Readonly<
        isLoaded: boolean;
        isLoadedError: boolean;
        model: IDomainModel;
        error: any;
    >;

    export interface IDomainStore 
        domain: DomainState;
    

    interface IDomainPageCompProps extends 
        RouteComponentProps<IDomainRoutingParams> 
        isLoadedError: boolean;
        isLoaded: boolean;
        resources: any;
    

    import  connect  from 'react-redux';
    import  IDomainPageCompProps, DomainPage  from '...';
    import  IDomainStore  from '../store';
    import  RouteComponentProps, withRouter  from 'react-router';
    import  IDomainRoutingParams  from '../types/routing-params';

    interface IOwnProps extends RouteComponentProps<IDomainRoutingParams> 
        resources;
    

    const connected = connect<IDomainPageCompProps, , IOwnProps, IDomainStore>(
        (store: IDomainStore, ownProps: IOwnProps) => 
            return 
                ...ownProps,
                isLoaded: store.domain.isLoaded,
                isLoadedError: store.domain.isLoadedError
            ;
        ,
        (/* dispatch */) => 
            return ;
        
    )(DomainPage);
    export const DomainPageContainer = withRouter(connected);


    // .jsx
    <Provider store=store>
        <DomainPageContainer resources=... />
    </Provider>

【讨论】:

以上是关于在 .NET Core 2.0 reactredux 模板项目中使用 react redux 和 typescript 将组件与 OwnProps 连接起来的主要内容,如果未能解决你的问题,请参考以下文章