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

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

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

编辑:我能够使用下面的代码将其 99% 地工作。一切都在编译时运行,但在父组件中使用时会发出警告,指出属性计数丢失。实际执行时,一切都按预期工作。此计数属性位于 CounterStore 中,并在按下按钮时正确递增。一切正常,但我不确定如何摆脱 typescript 警告。我在某处做错了类型定义吗?原始模板将 as 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 组件)使用 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 接口(interface),将其包含在 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 接口(interface),但我不确定该怎么做。这是 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>;
}

最佳答案

使用一些:

    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>

关于reactjs - 在 .NET Core 2.0 reactredux 模板项目中使用 react redux 和 typescript 将组件与 OwnProps 连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47641904/

相关文章:

angularjs - React 与 Angular : Slow rendering with React

javascript - React-native 异步获取返回 null

typescript - 将样式组件添加到项目原因中。找不到命名空间 'NodeJS'

visual-studio - Visual Studio TypeScript - 找不到模块

javascript - 在卡片背面 react Flip-Card 错误

javascript - 当我尝试运行 grunt-react 任务时,它会挂起

typescript - 在原子 : What does the little red triangle and dotted line tell me?

javascript - 我怎样才能停止 redux Action ?当我出错时

reactjs - Redux router - 刷新后如何重放状态?

javascript - 使用 Mocha 设置 JSDOM