乐趣区

关于前端:使用-React-和-TypeScript-something-编写干净代码的10个必知模式

React 是一个 JavaScript 库,它是当今最风行和行业当先的前端开发库。

JavaScript 是一种涣散的类型化语言,因而,它捕捉了运行时。这样做的后果就是 JavaScript 谬误被捕取得十分晚,这可能导致重大的 bug。

当然 React 作为一个 JavaScript 库,也继承了这个问题。

洁净代码 (Clean code)”) 是一种统一的编程格调,它使代码更容易编写、读取和保护。任何人都能够编写计算机能够了解的代码,然而优良的开发人员能够编写人类能够了解的洁净的代码。

洁净的代码是一种以读者为核心的开发格调,它进步了咱们的软件品质和可维护性。

编写洁净代码须要编写具备清晰和简略的设计模式的代码,这使得人们能够轻松地浏览、测试和保护代码。因而,洁净的代码能够升高软件开发的老本。这是因为编写洁净的代码所波及的准则,打消了技术债权。

在本文中,咱们将介绍一些在应用 React 和 TypeScript 时应用的有用模式。

💡 为了让您的团队更容易地放弃代码衰弱并优先解决技术债权工作,请尝试应用 Stepsize 的 VS Code 和 JetBrains 扩大。它们帮忙工程师创立技术问题,将它们增加到迭代 中,并继续解决技术债权——而不来到编辑器。

当初让咱们来理解一下在应用 React 和 Typescript 时利用的 10 个有用模式:

1. 应用默认导入来导入 React

思考上面的代码:

import * as React from "react";

尽管下面的代码能够工作,然而如果咱们不应用 React 的所有内容,那么导入它们是令人困惑的,也不是一个好的做法。一个更好的模式是应用如下所示的默认导出:

import React, {useContext, useState} from "react";

应用这种办法,咱们能够从 React 模块中解构咱们须要的货色,而不是导入所有的内容。

留神: 要应用这个选项,咱们须要配置 tsconfig.json 文件,如下所示:

{
  "compilerOptions": {"esModuleInterop": true"}
}

在下面的代码中,通过将 esModuleInterop 设置为 true,咱们启用了 allowSyntheticDefaultImports,这对于 TypeScript 反对咱们的语法十分重要。

2. 类型 申明 要在运行时实现之前

思考上面的代码:

import React, {Component} from "react";

const initialState = {count: 1}
const defaultProps = {name: "John Doe"}

type State = typeof initialState;
type Props = {count?: number} & typeof defaultProps

class Counter extends Component {

   static defaultProps = defaultProps;
   state = initialState;

   // ...

}

如果咱们将运行时申明和编译时申明离开,并且编译时申明在运行时申明之前,那么下面的代码能够更清晰、更易读。

思考上面的代码:

import React, {Component} from "react";

type State = typeof initialState;
type Props = {count?: number} & typeof defaultProps

const initialState = {count: 1}
const defaultProps = {name: "John Doe"}

class Counter extends Component {

   static defaultProps = defaultProps;
   state = initialState;

   // ...

}

当初,初看起来,开发人员晓得组件 API 是什么样的,因为代码的第一行分明地显示了这一点。

此外,咱们还将编译时申明与运行时申明离开。

3. 给 children 提供明确的 props

Typescript 反映了 React 如何解决 children props,办法是在 react.d.ts 中为函数组件和类组件将其正文为可选的。

因而,咱们须要明确地为 children 提供一个 props 类型。然而,最好总是用类型明确地正文 children 的 props。在咱们心愿应用 children 进行内容投影的状况下,这是十分有用的,如果咱们的组件不应用它,咱们能够简略地应用 never 类型来正文它。

思考上面的代码:

import React, {Component} from "react";
// Card.tsx
type Props = {children: React.ReactNode}

class Card extends Component<Props> {render() {const {children} = this.props;
        return <div>{children}</div>;
    }
}

上面是一些正文 children 的 props 类型:

  • ReactNode | ReactChild | ReactElement
  • 对于原始类型能够应用:string | number | boolean
  • 对象和数组也是无效的类型
  • never | null | undefined – 留神:不倡议应用 nullundefined

4. 应用类型推断来定义组件状态或 DefaultProps

看上面的代码:

import React, {Component} from "react";

type State = {count: number};

type Props = {someProps: string & DefaultProps;}

type DefaultProps = {name: string}

class Counter extends Component<Props, State> {static defaultProps: DefaultProps = {name: "John Doe"}
    state = {count: 0}

    // ...
}

尽管下面的代码能够工作,然而咱们能够对它进行以下改良: 启用 TypeScript 的类型零碎来正确推断readonly 类型,比方 DefaultPropsinitialState

为了避免因为意外设置状态而导致的开发谬误: this.state = {}

思考上面的代码:

import React, {Component} from "react";

const initialState = Object.freeze({count: 0})
const defaultProps = Object.freeze({name: "John Doe"})

type State = typeof initialState;
type Props = {someProps: string} & typeof defaultProps;

class Counter extends Component<Props, State> {
    static readonly defaultProps = defaultProps;
    readonly state  = {count: 0}

    // ...
}

在下面的代码中,通过解冻 DefaultPropsinitialState,TypeScript 类型零碎当初能够将它们推断为 readonly 类型。

另外,通过在类中将动态 defaultProps 和状态标记为 readonly,咱们打消了下面提到的设置状态引起运行时谬误的可能性。

5. 申明 Props/State 时应用类型别名(type),而不是接口(interface)

尽管能够应用 interface,但为了一致性和清晰性起见,最好应用 type,因为有些状况下interface 不能工作。例如,在后面的示例中,咱们重构了代码,以使 TypeScript 的类型零碎可能通过从实现中定义状态类型来正确推断 readonly类型。咱们不能像上面的代码那样应用这个模式的interface:

// works
type State = typeof initialState;
type Props = {someProps: string} & typeof defaultProps;

// throws error
interface State = typeof initialState;
interface Props = {someProps: string} & typeof defaultProps;

此外,咱们不能用联结和交加创立的类型扩大interface,因而在这些状况下,咱们必须应用 type

6. 不要再 interface/type 中应用办法申明

这能够确保咱们的代码中的模式一致性,因为 type/interface 推断的所有成员都是以雷同的形式申明的。另外,--strictFunctionTypes 仅在比拟函数时工作,而不适用于办法。你能够从这个 TS 问题中失去进一步的解释。

// Don't do
interface Counter {start(count:number) : string
  reset(): void}

// Do
interface Counter {start: (count:number) => string
  reset: () => string}

7. 不要应用 FunctionComponent

或者简称为 FC 来定义一个函数组件。

当应用 Typescript 和 React 时,函数组件能够通过两种形式编写:

  1. 像一个失常函数一样,如上面的代码:
type Props = {message: string};

const Greeting = ({message}: Props) => <div>{message}</div>;
  1. 应用 React.FC 或者 React.FunctionComponent,像上面这样:
import React, {FC} from "react";

type Props = {message: string};

const Greeting: FC<Props> = (props) => <div>{props}</div>;

应用 FC 提供了一些劣势,例如对诸如 displayNamepropTypesdefaultProps 等动态属性进行类型检查和主动实现。然而它有一个已知的问题,那就是毁坏 defaultProps 和其余属性: propTypescontextTypesdisplayName

FC 还提供了一个隐式类型的 children 属性,也有已知的问题。此外,正如后面探讨的,组件 API 应该是显式的,所以一个隐式类型的 children 属性不是最好的。

8. 不要对类组件应用构造函数

有了新的 类属性 提议,就不再须要在 JavaScript 类中应用构造函数了。应用构造函数波及调用 super ()和传递 props,这就引入了不必要的样板和复杂性。

咱们能够编写更简洁、更易于保护的 React class 组件,应用类字段,如下所示:

// Don't do
type State = {count: number}
type Props = {}

class Counter extends Component<Props, State> {constructor(props:Props){super(props);
      this.state = {count: 0}
  }
}

// Do
type State = {count: number}
type Props = {}

class Counter extends Component<Props, State> {state = {count: 0}
}

在下面的代码中,咱们看到应用类属性波及的样板文件较少,因而咱们不用解决 this 变量。

9. 不要在类中应用 public 关键字

思考上面的代码:

import {Component} from "react"

class Friends extends Component {public fetchFriends () {}
  public render () {return // jsx blob}
}

因为类中的所有成员在默认状况下和运行时都是 public 的,因而不须要通过显式应用 public 关键字来增加额定的样板文件。相同,应用上面的模式:

import {Component} from "react"

class Friends extends Component {fetchFriends () {}
  render () {return // jsx blob}
}

10. 不要在组件类中应用 private

思考上面的代码:

import {Component} from "react"

class Friends extends Component {private fetchProfileByID () {}

  render () {return // jsx blob}
}

在下面的代码中,private 只在编译时将 fetchProfileByID 办法私有化,因为它只是一个 Typescript 模仿。然而,在运行时,fetchProfileByID 办法依然是公共的。

有不同的办法使 JavaScript 类的属性 / 办法私有化,应用下划线 (\_) 变数命名准则如下:

import {Component} from "react"

class Friends extends Component {_fetchProfileByID () {}

  render () {return // jsx blob}
}

尽管这并没有真正使 fetchProfileByID 办法成为公有办法,但它很好地向其余开发人员传播了咱们的用意,即指定的办法应该被视为公有办法。其余技术包含应用 WeakMap、Symbol 和限定作用域的变量。

然而有了新的 ECMAScript 类字段的提议,咱们能够通过应用公有字段轻松优雅地实现这一点,如下所示:

import {Component} from "react"

class Friends extends Component {#fetchProfileByID () {}

  render () {return // jsx blob}
}

而且 TypeScript 反对 3.8 及以上版本公有字段的新 JavaScript 语法。

附加:不要应用 enum

只管 enum 在 JavaScript 中是一个保留字,然而应用 enum 并不是一个规范的习用 JavaScript 模式。

然而如果你应用的是 c # 或者 JAVA 这样的语言,那么应用 enum 可能是十分迷人的。然而,还有更好的模式,比方应用编译类型文字,如下所示:

// Don't do this
enum Response {
  Successful,
  Failed,
  Pending
}

function fetchData (status: Response): void => {// some code.}

// Do this
type Response = Sucessful | Failed | Pending

function fetchData (status: Response): void => {// some code.}

总结

毫无疑问,应用 Typescript 会给你的代码减少很多额定的样板文件,然而这样做的益处是十分值得的。

为了使您的代码更洁净、更好,不要遗记实现一个强壮的 TODO/issue 过程。它将帮忙您的工程团队取得技术债权的可见性,在代码库问题上进行合作,并更好地布局冲刺。

本文译自:https://dev.to/alexomeyer/10-…

退出移动版