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

时间:2017-12-04 21:02:41

标签: reactjs typescript redux typescript-typings asp.net-core-2.0

可在此处找到源代码:https://github.com/cvanem/ASPNETCoreReact16Redux

编辑:我能够使用以下代码获得99%。一切都在编译时运行,但是当在父组件中使用时,它会发出一个警告,指出缺少属性计数。实际执行时,一切都按预期工作。此计数属性位于CounterStore中,并在按下按钮时正确递增。一切正常,但我不知道如何摆脱打字稿警告。我在某个地方做错了吗?原始模板将typeof Counter添加到connect语句的末尾,如下所示:

    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组件被父组件使用时,仍然会给出typescript警告:(例如):

    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中并将其添加到底部的connect调用中。这是组件代码:

    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接口的注释,但我不确定如何做到这一点。以下是@ types / react-redux的index.d.ts文件中的部分:

    /**
     * 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>;
}

1 个答案:

答案 0 :(得分:0)

使用一些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>