美文网首页
用 typescript 写 react component 的

用 typescript 写 react component 的

作者: 472abb2e4941 | 来源:发表于2018-08-28 17:33 被阅读8次

    分享一篇讲 react ts 中模式的文章, 写的非常好
    原文地址: https://levelup.gitconnected.com/ultimate-react-component-patterns-with-typescript-2-8-82990c516935

    如果对下面代码不理解, 建议看下 https://basarat.gitbooks.io/typescript/content/docs/getting-started.html typescript deep dive 小白书

    Stateless Component

    按下面方式写 stateless component , 这样就不会报错了

    import React, { MouseEvent, ReactNode } from 'react'
    type Props = { 
     onClick(e: MouseEvent<HTMLElement>): void
     children?: ReactNode 
    }
    const Button = ({ onClick, children }: Props) => (
      <button onClick={onClick}>{children}</button>
    )
    

    但是我们可以做的更好,在 @types/react 预先定义了 type SFC<P>,它是 StatelessComponent<P> 的别名,它预先定义了 children 和一些其他的东西,譬如 defaultProps, displayName , 这样你就不用每次自己写,最后如下:

    import React, { MouseEvent, SFC } from 'react'
    type Props = { 
     onClick(e: MouseEvent<HTMLElement>): void
    }
    const Button: SFC<Props> = ({ onClick, children }) => (
      <button onClick={handleClick}>{children}</button>
    )
    

    Stateful Component

    让我们写一个计时器的 stateful component

    首先我们定义下 initialState

    const initialState = { clicksCount: 0 }
    

    这样作我们不用分别管理 types 和 implementation(实现), 我们只有一个源, 那就是 implementation

    type State = Readonly<typeof initialState>
    

    同样注意 type 被明确 mapped 使得所有属性都是只读. 我们同样需要在我们的 class 上把 state 属性定义为 State type 类型, 并且设置成只读

    readonly state: State = initialState
    

    为什么要这样做? 我们知道我们不能像下面的方式直接更改 React 的 state

    this.state.clicksCount = 2 
    this.state = { clicksCount: 2 }
    

    这会在运行时抛出错误, 但是不是在编译的时候. 通过遍历 State type 申明的属性, 并设置成 readonly, 并且把组件的 state 也设置成 only, TS 在我们在我们写代码的时候就会立马知道

    如下所示

    请注意红线报错

    整个组件实现:

    我们的 Container 没有任何的 Props , 所以我们必须传入 object 做为第一个泛型参数, 使用 State type 作为第二个泛型参数

    image.png

    你可能以及注意到, 我把 update state 的方法抽到 class 外面了. 这是一个经常用到的模式, 这使得我们可以很容易的测试, 而不需要关心 render 层逻辑. 并且因为我们使用 typescript, 并且 map State 属性成只读, 这同样能防止我们在这些方法里面对 state 做任何更改

    const decrementClicksCount = (prevState: State) 
                          => ({ clicksCount: prevState.clicksCount-- })
    
    // Will throw following complile error:
    //
    // [ts]
    // Cannot assign to 'clicksCount' because it is a constant or a read-only property.
    

    这看上去不错吧.

    Default Props

    让我们扩展我们的 Button component, 添加一个 color prop , 它是一个 string 类型

    type Props = { 
      onClick(e: MouseEvent<HTMLElement>): void
      color: string 
    }
    

    如果我们想定义 defaultProps, 我们可以通过 Button.defaultProps = {...} 设定

    By doing that we need to change our Props type definition to mark props that are default as optional.

    So something like this ( notice the ? operator )
    然而定义了默认属性, 我们必须更改 Props type, 因为已经设定了默认属性的可以不用传了, 变成了可选

    就像下面那样, 注意 ?

    type Props = { 
      onClick(e: MouseEvent<HTMLElement>): void
      color?: string 
    }
    

    然后我们的组件变成了这个样子:

    const Button: SFC<Props> = ({ onClick: handleClick, color, children }) => (
      <button style={{ color }} onClick={handleClick}>
        {children}
      </button>
    )
    

    这个简单的示例看上去没有问题, 但是实际这里面有个坑. 因为我们使用了严格模式, 我们的 color 设置成 optional prop, 它变成了 undefined | string 的联合类型.

    但假如我们想对这种 optional props 做一些操作, TS 会抛出一个错误, 因为它不知道这个属性预先定义在了 defaultProps 上

    color 属性有 default 值, 但 ts 仍然认为它可能是 undefined

    为了使得 TS 不报错, 我们这里有三个做法:

    • 使用 ! 操作符, 明确告诉 compiler 这个属性不是 undefined , 尽管这个属性是 optional. 像这样: <button onClick={handleClick!}>{children}</button>
    • 使用条件判断操作符, 使编辑器明白这种特殊的 props 不会为 undefined: <button onClick={handleClick ? handleClick: undefined}>{children}</button>
    • 写一个可复用的 withDefaultProps 高阶组件, 这个高阶组件会更新我们 props type 的定义, 并且会设定我们默认的 props. 这是最明确的解决方案, 在我看来

    我们可以很容易实现我们的高阶函数(得益于 TS2.8 内置定义的 mapped types):

    image.png

    现在我们可以使用 withDefaultProps 高阶组件去定义我们的默认 props了, 这也同样能解决之前的问题,

    image.png

    或者直接写成内联 (注意, 我们不需要)

    image.png

    现在 Button Props 被定义成正确的类型了, defaultProps 体现出来了

    相关文章

      网友评论

          本文标题:用 typescript 写 react component 的

          本文链接:https://www.haomeiwen.com/subject/juxsvftx.html