使用 TS 编写 React 项目(1)

引言:TypeScript是一种由微软开发的自由和开源的编程语言。它是JavaScript的一个严格超集,并添加了可选的静态类型和使用看起来像基于类的面向对象编程语法操做Prototype。所以,咱们能够像编写强类型语言(java, dart)等编码方式来编写javascript 应用程序。javascript

如今各种前端项目都对 ts 进行了支持,如下是 ts 在 react 中使用的时候 各种组件使用方法。记录并分享给大伙。前端

类组件(webComponents 组件)

能够如下方法去编写一个 tsx 的 webComponent 组件。 该组件,将能够在不使用 Prop-types 类型检测库的状况下,对咱们传入组件的 Props 进行约束,而且在编译器 上能够极其友好、明确的提示咱们编写的组件的所接受的类型和方法等。java

type IPrps = {
  message: string;
};

type IState = {
  count: number; 
};

class App extends React.Component<IPrps, IState> {
  state: IState = {
    count: 0
  };
  
  render() {
    return (
      <div> {this.props.message} {this.state.count} </div>
    );
  }
}

复制代码

React 官网对于 Component 的类型定义以下。react

interface Component<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> { }
复制代码

经过定义文件咱们能够这样去编写咱们的组件:web

Component 接受泛型类 P & S & SS, 接受到 P 后,会将组件推导为 Readonly<{ children?: ReactNode }> & Readonly<P> 交叉类型,而且对 props 类型进行只读保护,咱们没必要特地在 Props 中特地去声明 children,也不须要在添加 Readonly 标识,由于这些都是自动的。编程


函数式组件 (Function Components)

函数式组件也并不违反开闭原则,所以咱们能够像编写一个函数去约束参数类型同样去使用函数式组件。 能够经过如下方式去编写一个 tsx 版本的函数式组件:redux

type IProps = {
    onPress: () => void;
    iconStyle?: any;
    url: string,
};

const TouchIcon: React.FC<IProps> = (props: IProps) => {
    return (
        <div onClick={props.onPress}>
            <p>{props.url}</p>
            <p>{props.iconStyle}</p>
        </div>
    );
};
复制代码

React.FC 为 React.FunctionComponent 的简写,在使用和类型定义上二者没有任何区别。react-native

此外,React.FC 组件,会在编译阶段自动为其静态属性,好比 displayName, propTypes, defaultProps 等进行类型补全。bash

    React SFC 组件
(Stateless Function Components)

在 React 16.8 之前,咱们没法在函数式组件中去定义组件内部的状态,此时咱们能够认为一个组件是 stateless(无状态的) 的,可是因为 React 16.8react-native 在 0.59 release 版本后才支持 Hooks 之后推出 React Hooks。stateless function components 这个定义再也不准确。所以,咱们如今没必要在声明 React.SFC 了。微信


hooks 组件

咱们能够经过下面的方法去编写 tsx 版本的 hooks 代码:

export type RecordItem = {
    title: string;
    total: number;
    id: number;
    color: string;
};

export const recordList: RecordItem[] = [
    {
        id: 1,
        total: 1500,
        title: "总完成课时",
        color: "rgb(20,125,255)",
    }, {
        id: 2,
        total: 30,
        title: "总完成课时",
        color: "rgb(255,96,96)",
    }
];

export const RecordWidget: React.SFC = () => {

    const [list, setList] = React.useState<RecordItem[]>(recordList);

    return ( 
        <>
            {
                list.map((item: RecordItem) => {
                    return (
                        <View style={{ backgroundColor: item.color }}>
                            <Text>{item.title}</Text>
                            <Text>{item.total}</Text>
                            <Text>{item.title}</Text>
                        </View>
                    );
                })
            }
        </>
    );
};

复制代码

若是须要使用默认值, 能够经过
const [list, setList] = React.useState<RecordItem[] | [] | null>(recordList); 来设置异常默认。

useRef

const ref = useRef<HTMLElement | null>(null);
复制代码

useReducer

type AuthState = {};
type Action =
  | { type: "FETCH_SUCCESS"; payload: any }
  | { type: "FETCH_ERROR"; payload: string };

export function reducer(state: AuthState, action: Action): AuthState {
  switch (action.type) {
    case "FETCH_SUCCESS":
      return {
        ...state,
        one: action.payload
      };
    case "FETCH_ERROR":
      return {
        ...state,
        two: action.payload
      };
    default:
      return state;
  }
}
复制代码

事件处理

在定义 React 各种事件时候,使用 ts 咱们能够根据业务场景去定义不一样的事件类型来对代码进行静态检查。

好比某个表单提交事件:

<form
  ref={formRef}
  onSubmit={(e: React.SyntheticEvent) => {
    e.preventDefault();
  }}
</form>
复制代码

如此即可以拿到全部改事件类型下的方法和属性了。

根据业务咱们须要选择不一样的事件类型处理不一样的业务场景,下面是几个常见事件的列举。

事件描述 事件类型 泛型类型
合成 事件 SyntheticEvent null
Change 事件 ChangeEvent <T = Element>
剪贴板事件 ClipboardEvent <T = Element>
拖拽事件 DragEvent <T = Element>
键盘事件 KeyboardEvent <T = Element>
鼠标事件 MouseEvent <T = Element>
触摸事件 TouchEvent <T = Element>
滚轮事件 WheelEvent <T = Element>
动画事件 AnimationEvent <T = Element>
过渡事件 TransitionEvent <T = Element>

Tips: React SyntheticEvent(合成事件)对象会被重用,为了性能缘由,若是须要异步访问该事件对象,能够经过调用 event.persist() 来获取事件对象。


Redux

关于 action 的 ts 版本的推导和类型保护,有不少种方法和各类泛型版本。下面分享的是我在项目使用的方式供你们参考。

redux action 被设计为描述事件动做,并能够承接动做数据的载体(payload),下面是 redux 定义的 action 类型

在实际业务场景中,咱们有时须要对 payload 载体进行约束,能够经过如下方式:

import { Action as ReduxAction } from 'redux';

export interface Action<T extends string, P = any> extends ReduxAction<T> {
    payload?: P;
}
复制代码

自定义 接口(interface)action 继承自 ReduxAction,拓展 payload 属性。使用方法以下:

此时,咱们的 action 肯定了入参以及返回的类型。因为 payload 是可选属性,所以在没有 payload 载体传入时,只须要这样定义便可:

上面的方法只是咱们对 action 的类型进行了定义,当咱们业务有多少个请求和交互要发出,可能就会有多个 Action。你也能够定义一个函数来生成 Action,这个函数就叫 Action Creator。

关于 action Creator 可使用函数的重载来实现对象 action 和 payload 的支持,Action Creator 定义以下:

export function createAction<T extends string>(type: T): Action<T, void>;
export function createAction<T extends string, P>(
    type: T,
    payload: P
): Action<T, P>;
export function createAction<T extends string, P>(type: T, payload?: P) {
    return typeof payload === 'undefined' ? { type } : { type, payload };
}
复制代码

经过函数的重载,咱们能够定义工厂方法来生产 action,并自动完成对 action 动做的校验。

上面是对于 redux action 动做的校验及推导的二种方式,有更多简单的方法,以上仅供参考

获取全部的 action 种类

既然对 action 进行约束,咱们须要在代码上获取到当前全部的 action 种类。一般咱们会在 store 仓库中新建 action 目录,在里面存放全部的 action 并在 index 中进行导出。像这样:

由于咱们约定将全部的 action 定义在 action/ 文件夹下,所以咱们能够这样去获取全部的 action 种类:

import * as actions from "@action/index";

type AllActions = keyof typeof actions[keyof typeof actions];
复制代码

获取全部的 redux state 类型

只需定义 类型 去取出全部的 state 种类就行了,像这样:

export type AllState =
    keyof AuthState | keyof CommonState |
    keyof ComFetchState | keyof ComNetworkState |
    keyof OperationState;
复制代码


上面分享的都是一些简单的 React 在结合 ts 使用时的打开方式。有些复杂的关于 hoc 或者是更详细的业务场景的推导,能够添加下面的微信群组。你们一块儿讨论(不开车,只谈论技术哈)。

相关文章
相关标签/搜索