在 .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 连接起来的主要内容,如果未能解决你的问题,请参考以下文章

.Net Core 2.0 Windows 服务

Azure Functions .NET Core 中的 EF Core 2.0 连接字符串

如何在新创建的 .NET Core 2.0 Web 应用程序中定位 .NET Standard 2.0?

Json.Net 在.Net Core 2.0 中序列化DataSet 问题

在 .NET Core 2.0 中使用 WCF 服务

聊一聊如何在.NET Core中使用Nacos 2.0