跳至主要内容

钩子语法

钩子语法是 React 钩子的头等语法和类型检查支持,将钩子作为 React 语言中的独立实体引入,这些实体在语法和语义上与普通函数不同,并使用 Flow 来强制执行React 规则不被违反。

基本用法

编写函数和钩子之间的主要区别在于 hook 关键字

1import {useState, useEffect} from 'react';2
3hook useOnlineStatus(initial: boolean): boolean {4  const [isOnline, setIsOnline] = useState(initial);5  useEffect(() => {6    // ...7  }, []);8  return isOnline;9}

钩子可以像普通函数一样调用

1import * as React from 'react';2
3hook useOnlineStatus(): boolean {4    return true;5}6
7component StatusBar() {8  const isOnline = useOnlineStatus();9  return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;10}

钩子可以像普通函数一样导出

1export hook useNamedExportedHook(): boolean {2    return true;3}4
5export default hook useDefaultExportedHook(): boolean {6    return true;7}

钩子类型注解

在某些情况下,您可能希望将值定义为具有钩子类型的类型。由于函数类型和钩子类型不兼容(稍后将详细介绍!),我们还引入了钩子类型注解的新语法,它只是现有的函数类型注解,但前面加上 hook。

export const useGKOnlineStatus: hook (boolean) => boolean = 
experiment('show_online_status')
? useOnlineStatus
: useAlwaysOnlineStatus

使用钩子语法强制执行 React 规则

使用钩子语法,我们现在可以明确地从语法上区分钩子和非钩子。Flow 将使用此信息来强制执行许多钩子规则和React 规则

防止不安全变异

根据React 规则,在组件渲染时不允许读取或写入 ref,并且其他钩子的返回值(尤其是 useState)不能安全地直接变异。通过让 Flow 了解钩子作为一等概念,我们现在可以在许多情况下检测到这些问题并尽早引发错误,而不是依赖测试来发现它们。

1import {useState, useEffect, useRef} from 'react';2import * as React from 'react';3
4component MyComponent() { 5  const ref = useRef<?number>(null);6  const [state, setState] = useState<{ val: number }>({val: 0});7
8  state.val = 42; // Flow error: cannot mutate return value of hook
9 10 return (11 <div>12 {ref.current /* Flow error: cannot read ref during rendering */}
13 </div>14 );15}
8:9-8:11: Cannot assign `42` to `state.val` because property `val` is not writable. The return value of a React hook [1] cannot be written to. [react-rule-hook-mutation]
12:8-12:10: Cannot read `current` from `ref` [1] because `ref` values may not be read during render. (https://reactjs.ac.cn/reference/react/useRef). [react-rule-unsafe-ref]

Flow 目前阻止组件 props 在组件内被修改。钩子语法允许我们将此检查扩展到钩子,并将让我们在钩子声明中发生非法变异时检测到错误并引发错误。

1hook useIllegalMutation(values: Array<number>) {2  values[0] = 42; // Flow error: mutating argument to hook
3 // ...4}
2:3-2:11: Cannot assign `42` to `values[0]` because read-only arrays cannot be written to. React hook arguments [1] and their nested elements cannot be written to. [react-rule-unsafe-mutation]

防止条件钩子调用

钩子规则禁止条件调用钩子。这由React 的 ESLint 插件涵盖,但现在 Flow 也会检查这些违规行为。

1hook useOnlineStatus(): boolean {2    return true;3}4
5component StatusBar(shouldShowOnlineStatus: boolean) {6  if (shouldShowOnlineStatus) {7    const onlineStatus = useOnlineStatus();
8 }9 10 return null;11}
7:26-7:42: Cannot call hook [1] because React hooks cannot be called in conditional contexts. [react-rule-hook]

防止钩子和函数混淆

钩子和普通函数之间的区别反映在 Flow 类型系统中。由于钩子和函数必须遵守不同的属性,因此将定义为钩子的值传递到期望函数类型的职位中是 Flow 错误,而将普通 JavaScript 函数传递到期望钩子的职位中也是错误。

1import {useState, useEffect} from 'react';2
3hook useMultiplier(x: number): number {4  const [y, setY] = useState(1);5  useEffect(() => { setY(0) })6  return x * y;7}8
9component Mapper(args: Array<number>) {10  const multArgs = args.map(useMultiplier);
11 12 return multArgs;13}
10:29-10:41: Cannot call `args.map` with `useMultiplier` bound to `callbackfn` because function [1] is a React hook but function type [2] is not a hook. React hooks and other functions are not compatible with each other, because hooks cannot be called conditionally. [react-rule-hook-incompatible]

此外,Flow 强制执行钩子和组件内部具有钩子式名称的被调用者确实是钩子。我们还确保普通函数定义内部的被调用者永远不是钩子。

1hook useHook() { return null }2
3function regularJavascript() {4  const x = useHook(); // Flow error: cannot call a hook outside of a component or hook
5}6 7component Component() { 8 const renamedHook = useHook;9 renamedHook(); // Flow error: cannot call a hook whose name does not begin with `use`
10 11 return null;12}
4:13-4:21: Cannot call hook [1] because React hooks can only be called within components or hooks. [react-rule-hook]
9:3-9:15: Cannot call hook because callee [1]'s name does not conform to React hook rules. Hook names must begin with `use` followed by a capitalized letter. [react-rule-hook]