framework
framework/react/hook/order-and-rules.md
medium
react-hook-order-and-rules
Hooks 调用顺序:state → ref → derived(useMemo) → effect → animation → callback。Use when 写 React 组件 / 改 .tsx 文件 / 评审涉及 `order-and-rules` 的 PR。
usestateuserefuseeffectusememousecallback调用顺序
paths
frontend/src/**/*.tsxfrontend/src/hooks/**/*.ts
React · Hooks 顺序与规则
标准顺序
function MyComponent(props: Props) {
// 1. state
const [hovered, setHovered] = useState(false);
const [zoom, setZoom] = useState(1);
// 2. ref
const containerRef = useRef<HTMLDivElement>(null);
const timerRef = useRef<NodeJS.Timeout | null>(null);
// 3. derived (useMemo / 自定义 hook 返回值)
const subtitle = useMemo(
() => `${props.subject} · ${props.grade}`,
[props.subject, props.grade]
);
const { data, isLoading } = useQuery(...);
// 4. effect
useEffect(() => {
// 仅 DOM 同步 / 订阅 / 清理
return () => timerRef.current && clearTimeout(timerRef.current);
}, []);
// 5. animation (useGSAP)
useGSAP(() => {
gsap.to(containerRef.current, { scale: zoom, duration: D.micro });
}, { scope: containerRef, dependencies: [zoom] });
// 6. callback (useCallback / handlers)
const handleClick = useCallback(() => {
setHovered(true);
}, []);
// 7. render
return <div ref={containerRef} onClick={handleClick}>...</div>;
}
React 三铁律
- 顶层调用:不在循环 / 条件 / 嵌套函数里
- 只在 React 函数内:组件 / 自定义 hook
- 依赖完整:useEffect / useMemo / useCallback 的依赖数组必须列出所有引用的外部变量
反例
// ❌ 条件调用
function Bad({ flag }) {
if (flag) {
const [x] = useState(0); // ❌
}
}
// ❌ 依赖不完整
useEffect(() => {
fetch(`/api/users/${userId}`);
}, []); // userId 未列入 → 永远用第一次的 userId
// ❌ useEffect 拉数据
useEffect(() => {
fetch("/api/users").then(setUsers); // 用 useQuery
}, []);
ESLint 强制
{
"rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error"
}
}
自检
- [ ] Hook 都在顶层(无条件 / 循环)?
- [ ] 依赖数组完整?
- [ ] 顺序:state → ref → derived → effect → animation → callback?
- [ ] useEffect 不拉数据(用 useQuery)?