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
– 留神:不倡议应用null
和undefined
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
类型,比方 DefaultProps
和 initialState
。
为了避免因为意外设置状态而导致的开发谬误: 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}
// ...
}
在下面的代码中,通过解冻 DefaultProps
和 initialState
,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 时,函数组件能够通过两种形式编写:
- 像一个失常函数一样,如上面的代码:
type Props = {message: string};
const Greeting = ({message}: Props) => <div>{message}</div>;
- 应用 React.FC 或者 React.FunctionComponent,像上面这样:
import React, {FC} from "react";
type Props = {message: string};
const Greeting: FC<Props> = (props) => <div>{props}</div>;
应用 FC 提供了一些劣势,例如对诸如 displayName
、propTypes
和 defaultProps
等动态属性进行类型检查和主动实现。然而它有一个已知的问题,那就是毁坏 defaultProps
和其余属性: propTypes
,contextTypes
,displayName
。
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-…