Hook 語法
Hook 語法是 React hook 的一級語法和類型檢查支援,將 hook 帶入 React 語言中,作為與一般函式在語法和語意上不同的實體,並使用 Flow 來強制執行 React 規則,避免違反這些規則。
基本用法
撰寫函式和 hook 之間的主要差異在於 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}
Hook 可以像一般函式一樣呼叫
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}
Hook 可以像一般函式一樣匯出
1export hook useNamedExportedHook(): boolean {2 return true;3}4
5export default hook useDefaultExportedHook(): boolean {6 return true;7}
Hook 類型註解
在某些情況下,您可能希望將值定義為 hook 的類型。由於函式類型和 hook 類型不相容(詳見下文!),我們也推出 hook 類型註解的新語法,這只不過是現有的函式類型註解,但前面加上 hook。
export const useGKOnlineStatus: hook (boolean) => boolean =
experiment('show_online_status')
? useOnlineStatus
: useAlwaysOnlineStatus
使用 Hook 語法強制執行 React 規則
有了 hook 語法,我們現在可以在語法上明確區分 hook 和非 hook。Flow 會使用這些資訊來強制執行多項 hook 規則和 React 規則。
防止不安全的變異
根據React 規則,在元件渲染時不允許讀取或寫入 ref,而且其他 hook(特別是 `useState`)的回傳值根本無法安全地直接變異。透過讓 Flow 將 hook 視為一級概念,我們現在可以在許多情況下偵測這些問題並及早提出錯誤,而不是依賴測試來找出它們。
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://react.dev.org.tw/reference/react/useRef). [react-rule-unsafe-ref]
Flow 目前會防止元件屬性在元件內部被修改。hook 語法讓我們可以將此檢查擴充到 hook,並讓我們可以在 hook 宣告中發生非法變異時偵測並提出錯誤。
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]
防止條件式 hook 呼叫
hook 規則禁止有條件地呼叫 hook。這由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]
防止混淆 hook 和函式
hook 和一般函式之間的區別反映在 Flow 型別系統中。由於 hook 和函式必須遵守不同的屬性,因此將定義為 hook 的值傳遞到預期函式型別的位置,以及將一般 JavaScript 函式傳遞到預期 hook 的位置,都是 Flow 錯誤。
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 強制執行 hook 和元件內部具有類似 hook 名稱的被呼叫者確實是 hook。我們也確保一般函式定義內的被呼叫者永遠不會是 hook。
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]