# 只在顶层使用 Hooks

规范说明: 不要在循环、条件或嵌套函数中调用 Hooks。这是因为 React 依赖于 Hooks 的调用顺序来正确地关联 Hook 的状态。如果在这些 JavaScript 构造内部调用 Hooks,会打乱这个顺序,导致 bugs。

示例:

import React, { useState, useEffect } from "react";
const MyComponent: React.FC = () => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log(`You clicked ${count} times`);
  }, [count]);
  const handleClick = () => {
    if (count % 2 === 0) {
      setCount(count + 1);
    }
  };
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
};
export default MyComponent;

# 只在 React 函数组件和自定义 Hooks 中调用 Hooks

规范说明: 不应该在普通的 JavaScript 函数中调用 Hooks。这规则确保了所有的 stateful 逻辑都在 React 的控制之下,利于状态管理和组件的重用。

示例:

import React, { useState } from "react";
const MyComponent: React.FC = () => {
  const [name, setName] = useState("React");
  return (
    <div>
      <p>Hello, {name}!</p>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Enter your name"
      />
    </div>
  );
};
export default MyComponent;

# 为自定义 Hooks 使用命名规范

规范说明: 自定义 Hooks 应该以 “use” 开头,这不仅是一种约定,也让团队成员能够轻松地识别自定义 Hooks。例如, useFetchuseForm 等。

示例:

import { useState, useEffect } from "react";
const useFetch = (url: string) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    fetch(url)
      .then((response) => response.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      });
  }, [url]);
  return { data, loading };
};
const MyComponent: React.FC = () => {
  const { data, loading } = useFetch("https://api.example.com/data");
  if (loading) {
    return <p>Loading...</p>;
  }
  return <div>{JSON.stringify(data)}</div>;
};
export default MyComponent;

# 在使用 Hooks 时考虑数据流

规范说明: 使用 useStateuseEffect 等 Hooks 时,要考虑组件的数据流和副作用触发的条件。例如,依赖项数组在 useEffectuseMemouseCallback 中的正确使用是非常关键的,它决定了副作用函数或缓存的函数 / 值是否需要重新计算。

示例:

import React, { useState, useEffect } from "react";
const MyComponent: React.FC = () => {
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log(`Count has changed to ${count}`);
  }, [count]);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};
export default MyComponent;

# 遵循 Lint 规则

规范说明: 使用 ESLint 的 eslint-plugin-react-hooks 插件可以帮助自动检查上述规范,确保 Hooks 代码遵循 React 的最佳实践。这个插件会提醒关于依赖项的问题,以及是否违反了 Hooks 的使用规则。

示例:

// .eslintrc.json
{
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error", // 检查 Hook 的规则
    "react-hooks/exhaustive-deps": "warn" // 检查 effect 的依赖
  }
}

# 优化性能

规范说明: 合理使用 useCallbackuseMemo 来避免不必要的重新渲染,尤其是在将回调和其他值传递给子组件时。同时,正确使用依赖项数组,确保 useEffect 不会在每次渲染时都执行,除非确实需要根据依赖的变化来执行。

示例:

import React, { useState, useCallback } from "react";
const MyComponent: React.FC = () => {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("");
  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleClick}>Increment</button>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Enter some text"
      />
    </div>
  );
};
export default MyComponent;