Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用 Typescript 开发 React 组件 #27

Closed
fantasticit opened this issue Jun 14, 2019 · 0 comments
Closed

使用 Typescript 开发 React 组件 #27

fantasticit opened this issue Jun 14, 2019 · 0 comments
Labels

Comments

@fantasticit
Copy link
Owner

React Component in Typescript

最近学习了 react-redux-typescript-guide,在此记录一下。

安装类型定义

npm i -D @types/react @types/react-dom

React 中的类型

函数式组件

React.FC<props> | React.FunctionComponent<Props>

例如:

const MyComponent: React.FC<Props> = ...

类组件

React.Component<Props, State>

例如:

class MyComponent extends React.Component<Props, State> { ... }

高阶组件

React.ComponentType<Props>

例如:

const withState = <P extends WrappedComponentProps>(
  WrappedComponent: React.ComponentType<P>,
) => { ... }

组件 props

React.ComponentProps<typeof XXX>

例如:

type MyComponentProps = React.ComponentProps<typeof MyComponent>;

元素

React.ReactElement | JSX.Element;

例如:

const elementOnly: React.ReactElement = <div /> || <MyComponent />;

节点

React.ReactNode;

例如:

const elementOrPrimitive: React.ReactNode = 'string' || 0 || false || null || undefined || <div /> || <MyComponent />
const Component = ({ children: React.ReactNode }) => { ... }

样式对象

React.CSSProperties;

例如:

const style: React.CSSProperties = { color: "red" };
const element: React.ReactElement = <div style={style} />;

HTML 属性

React.HTMLProps;

例如:

const Input: React.FC<Props & React.HTMLProps<HTMLInputElement>> = props => { ... }

<Input accept={...} alt={...} />

事件处理句柄

React.ReactEventHandler<HTMLXXXElement>

例如:

const handleChange: React.ReactEventHandler<HTMLInputElement> = (evt) => { ... }
<input onChange={handleChange} />

事件对象

React.XXXEvent<HTMLXXXElement>

例如:

const handleChange = (ev: React.MouseEvent<HTMLDivElement>) => { ... }
<div onMouseMove={handleChange} />

组件实战

函数式组件

import * as React from "react";

type Props = {
  label: string,
  count: number,
  onIncrement: () => void
};

export const FCCounter: React.FC<Props> = props => {
  const { label, count, onIncrement } = props;

  return (
    <div>
      <span>
        {label}: {count}
      </span>
      <button onClick={onIncrement}>Increment</button>
    </div>
  );
};

使用:

<FCCounter
  label={"FCCounter"}
  count={this.state.count}
  onIncrement={() => this.setState({ count: this.state.count + 1 })}
/>

类组件

import * as React from "react";

type Props = {
  label: string;
  initialCount: number;
};

type State = {
  count: number;
};

export class StatefulCounter extends React.Component<Props, State> {
  static defaultProps = {
    initialCount: 0
  };

  readonly state: State = {
    count: this.props.initialCount
  };

  componentWillReceiveProps({ initialCount }: Props) {
    this.setState({ count: initialCount });
  }

  onIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    const { label } = this.props;
    const { count } = this.state;

    return (
      <div>
        <span>
          {label}: {count}
        </span>
        <button onClick={this.onIncrement}>Increment</button>
      </div>
    );
  }
}

使用:

<StatefulCounter label={"StatefulCounter"} initialCount={10} />

泛型组件

import * as React from "react";

export interface IGenericListProps<T> {
  items: T[];
  itemRenderer: (item: T) => React.ReactElement;
}

export class GenericList<T> extends React.Component<IGenericListProps<T>, {}> {
  render() {
    const { items, itemRenderer } = this.props;

    return <div>{items.map(itemRenderer)}</div>;
  }
}

使用:

interface IUser {
  name: string;
}
const users: IUser[] = [{ name: "Mike" }, { name: "Tom" }, { name: "David" }];
class UserList extends GenericList<IUser> {}

<UserList
  items={users}
  itemRenderer={({ name }) => <p key={name}>{name}</p>}
/>;

渲染回调组件

import * as React from "react";

export interface IMouseProviderProps {
  render: (state: IMouseProviderState) => React.ReactNode;
}

interface IMouseProviderState {
  readonly x: number;
  readonly y: number;
}

export class MouseProvider extends React.Component<
  IMouseProviderProps,
  IMouseProviderState
> {
  readonly state: IMouseProviderState = {
    x: 0,
    y: 0
  };

  handleMousemove = (evt: React.MouseEvent<HTMLDivElement>) => {
    this.setState({
      x: evt.clientX,
      y: evt.clientY
    });
  };

  render() {
    return (
      <div style={{ height: "100%" }} onMouseMove={this.handleMousemove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

使用:

<div style={{ height: 80, border: "1px solid #000" }}>
  <MouseProvider render={({ x, y }) => `x: ${x}, y: ${y}`} />
</div>

高阶组件

import * as React from "react";
import { Subtract } from "utility-types";

interface IInjectedProps {
  count: number;
  onIncrement: () => void;
}

export const withState = <BaseProps extends IInjectedProps>(
  _BaseComponent: React.ComponentType<BaseProps>
) => {
  const BaseComponent = (_BaseComponent as unknown) as React.ComponentType<
    IInjectedProps
  >;

  type HOCProps = Subtract<BaseProps, IInjectedProps> & {
    initialCount?: number;
  };
  type HOCState = { readonly count: number };

  return class HOC extends React.Component<HOCProps, HOCState> {
    readonly state: HOCState = {
      count: Number(this.props.initialCount) || 0
    };

    onIncrement = () => {
      this.setState({ count: this.state.count + 1 });
    };

    render() {
      return (
        <BaseComponent
          count={this.state.count}
          onIncrement={this.onIncrement}
          {...this.props}
        />
      );
    }
  };
};

使用:

const FCCounterWithState = withState(FCCounter);
<FCCounterWithState initialCount={20} label={"FCCounter withState"} />;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant