TypeScript连接具有有状态组件

时间:2017-10-21 09:24:15

标签: reactjs typescript redux react-redux

我创建了一个说明性组件Hello,我尝试在使用React-Redux的props装饰器时使用stateconnect的正确类型。

您可以在我创建的GitHub repository中进行游戏来说明这一点。

connect 用作装饰器,而是使用export default connect(mapStateToProps, mapDispatchToProps)(Hello)而组件本身未导出时,这可行。

type HelloStateProps = { enthusiasmLevel: number; };
type HelloDispatchProps = { incrementEnthusiasm: () => void; decrementEnthusiasm: () => void; };
type HelloOwnProps = { name: string; };
type HelloProps = HelloStateProps & HelloDispatchProps & HelloOwnProps;
type HelloState = { useHi: boolean; };

// I am getting an error here
@connect(mapStateToProps, mapDispatchToProps)
export default class Hello extends React.Component<HelloProps, HelloState> {
  state: HelloState = { useHi: false };

  onChangeGreetingButtonClick: React.MouseEventHandler<HTMLButtonElement> = event =>
    this.setState(state => ({ useHi: !state.useHi }))

  render() {
    const { useHi } = this.state;
    const { name, enthusiasmLevel } = this.props;
    return (
      <div className="hello">
        {useHi ? 'Hi' : 'Hello'} {name} x{enthusiasmLevel}
        <div>
          <button onClick={this.props.decrementEnthusiasm}>-</button>
          <button onClick={this.props.incrementEnthusiasm}>+</button>
          <button onClick={this.onChangeGreetingButtonClick}>Change greeting</button>
        </div>
      </div>
    );
  }
}

export function mapStateToProps({ enthusiasmLevel }: StoreState): HelloStateProps {
  return { enthusiasmLevel };
}

export function mapDispatchToProps(dispatch: Dispatch<EnthusiasmAction>): HelloDispatchProps {
  return Redux.bindActionCreators({ incrementEnthusiasm, decrementEnthusiasm }, dispatch);
}

尝试使用connect作为装饰器时遇到的错误如下:

Unable to resolve signature of class decorator when called as an expression.
  Type 'ComponentClass<Pick<HelloProps, "name">> & { WrappedComponent: ComponentType<HelloProps>; }' is not assignable to type 'typeof Hello'.
    Type 'Component<Pick<HelloProps, "name">, ComponentState>' is not assignable to type 'Hello'.
      Types of property 'state' are incompatible.
        Type 'Readonly<ComponentState>' is not assignable to type 'HelloState'.
          Property 'useHi' is missing in type 'Readonly<ComponentState>'.

当我完全剥离状态组件时,如下所示:

@connect(mapStateToProps, mapDispatchToProps)
export default class Hello extends React.Component<HelloProps> {
  render() {
    const { name, enthusiasmLevel } = this.props;
    return (
      <div className="hello">
        Hi {name} x{enthusiasmLevel}
        <div>
          <button onClick={this.props.decrementEnthusiasm}>-</button>
          <button onClick={this.props.incrementEnthusiasm}>+</button>
        </div>
      </div>
    );
  }
}

...我得到了这个错误:

Unable to resolve signature of class decorator when called as an expression.
  Type 'ComponentClass<Pick<HelloProps, "name">> & { WrappedComponent: ComponentType<HelloProps>; }' is not assignable to type 'typeof Hello'.
    Type 'Component<Pick<HelloProps, "name">, ComponentState>' is not assignable to type 'Hello'.
      Types of property 'render' are incompatible.
        Type '() => string | number | false | Element | Element[] | ReactPortal | null' is not assignable to type '() => Element'.
          Type 'string | number | false | Element | Element[] | ReactPortal | null' is not assignable to type 'Element'.
            Type 'null' is not assignable to type 'Element'.

问题出在哪里?我不明白错误信息。

在有状态的state中,connect应与useHi产生差异,对吧?那为什么甚至报道呢? ReadOnly<HelloState>中肯定不会遗漏ComponentState,但render来自哪里?

在无状态的JSX.Element中,当悬停时,会显示null返回类型。那么 <div class="form-group" id="assignto" style="display:none;"> <label class="control-label">Assigned To<span style="color:red;">*</span></label> <select class="form-control boxed" name="assignedto" id="assignedto" required> <option value="">Select</option> <option value="Male">Male</option> <option value="FeMale">FeMale</option> </select> </div> 是什么呢?

2 个答案:

答案 0 :(得分:6)

似乎是open issue

上面列出的一些解决方法如:

@(connect(mapStateToProps, mapDispatchToProps) as any)

答案 1 :(得分:1)

更高的Component Interface声明完成了将连接Type与组件状态和props Type结合起来的技巧。

connect.ts

import * as React from "react";
import {
  connect as originalConnect,
  MapStateToPropsParam,
  MergeProps,
  Options
} from "react-redux";
import { IState } from "./index";

export interface IDisPatchProps {
  [key: string]: () => void;
}

export type InferableComponentEnhancerWithProps<TInjectedProps, TNeedsProps> = <
  TComponent extends React.ComponentType<TInjectedProps & TNeedsProps>
>(
  component: TComponent
) => TComponent;

export interface IConnectProps {
  <TStateProps = {}, TDispatchProps = {}, TOwnProps = {}>(
    mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps, IState>,
    mapDispatchToProps?: IDisPatchProps
  ): InferableComponentEnhancerWithProps<
    TStateProps & TDispatchProps,
    TOwnProps
  >;

  <TStateProps = {}, TDispatchProps = {}, TOwnProps = {}, TMergedProps = {}>(
    mapStateToProps?: MapStateToPropsParam<TStateProps, TOwnProps, IState>,
    mapDispatchToProps?: IDisPatchProps,
    mergeProps?: MergeProps<
      TStateProps,
      TDispatchProps,
      TOwnProps,
      TMergedProps
    >,
    options?: Options<TStateProps, TOwnProps, TMergedProps>
  ): InferableComponentEnhancerWithProps<TMergedProps, TOwnProps>;
}

declare module "react-redux" {
  // tslint:disable-next-line
  interface Connect extends IConnectProps {}
}

export const connect = originalConnect as IConnectProps;


 ***ClassFile***

 @connect(
  (state: IState): IStateProps => ({
    count: state.counter.count,
    isLoading: state.counter.isLoading
  }),
  {
    decrement,
    increment
  }
)
export default class MyApp

链接: https://github.com/TomasHubelbauer/react-redux-typescript-connect-decorator-demo/blob/master/my-app/src/connect.ts

贷方转到: TomasHubelbauer https://github.com/TomasHubelbauer