# 只在顶层使用 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。例如, useFetch
、 useForm
等。
示例:
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 时考虑数据流
规范说明: 使用 useState
、 useEffect
等 Hooks 时,要考虑组件的数据流和副作用触发的条件。例如,依赖项数组在 useEffect
、 useMemo
和 useCallback
中的正确使用是非常关键的,它决定了副作用函数或缓存的函数 / 值是否需要重新计算。
示例:
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 的依赖 | |
} | |
} |
# 优化性能
规范说明: 合理使用 useCallback
和 useMemo
来避免不必要的重新渲染,尤其是在将回调和其他值传递给子组件时。同时,正确使用依赖项数组,确保 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; |