React Hooks
从 React 16.8 起,Hooks 让函数组件也能拥有状态、副作用等能力,还能把逻辑抽出来复用。如今新项目几乎全部采用 Hooks 写法,class 组件逐渐退居幕后。本节总览最核心的 useState、useEffect、useRef、useContext 以及自定义 Hook 的套路。
为什么需要 Hooks
class 组件时代有三大痛点:this 绑定容易出错;副作用与无关代码挤在同一个生命周期方法里;状态逻辑难以跨组件复用,只能靠渲染属性、高阶组件层层嵌套。Hooks 把「会变的状态、有副作用的操作」拆成独立的小函数,随取随用。
useState:给组件加状态
useState 返回一个数组:当前值与更新函数。调用更新函数后,组件会重新渲染。
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0); // 初始值为 0
return (
<button onClick={() => setCount(count + 1)}>
点击了 {count} 次
</button>
);
}
useEffect:处理副作用
数据请求、定时器、事件监听、修改标题等都属于副作用,统一放进 useEffect,它接收一个函数和依赖数组。
useEffect(() => {
document.title = `当前计数:${count}`; // 每次渲染后同步标题
}, [count]); // 依赖数组:count 变化才再次执行
useEffect(() => {
const timer = setInterval(() => console.log('tick'), 1000);
return () => clearInterval(timer); // 清理函数:卸载时自动执行
}, []); // 空数组:只在挂载时执行一次
依赖数组不写时每次渲染都执行;写 [] 只在挂载时执行一次;写 [a, b] 则在 a 或 b 变化时执行。需要清理的副作用(定时器、订阅等)应在函数里返回一个清理函数。
useRef 与 useContext
useRef 保存不触发渲染的值,常用于直接操作 DOM;useContext 跨层级读取共享数据,避免一层层传 props。
import { createContext, useContext, useRef } from 'react';
const Theme = createContext('light');
function Toolbar() {
const inputRef = useRef(null); // 初始值 null
const theme = useContext(Theme); // 直接取到 "dark"
return <input ref={inputRef} placeholder={`${theme} 主题的输入框`} />;
}
export default function App() {
return (
<Theme.Provider value="dark">
<Toolbar />
</Theme.Provider>
);
}
自定义 Hook 与使用规则
名字以 use 开头、内部调用其它 Hook 的函数就是自定义 Hook,能把一段逻辑抽出来给多个组件共用。
import { useEffect, useState } from 'react';
// 监听窗口宽度,任何组件都能用
function useWindowWidth() {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
return width;
}
export default function Panel() {
const width = useWindowWidth(); // 窗口变化时自动更新
return <p>窗口宽度:{width}px</p>;
}
使用 Hook 必须遵守两条铁律:其一,只在最顶层调用,不要在 if、循环或嵌套函数里调用,保证每次渲染的调用顺序一致;其二,只在函数组件或自定义 Hook 中调用,普通函数里调用会直接报错。
小结
Hooks 让函数组件集齐了状态、副作用与逻辑复用三种能力:状态交给 useState、副作用交给 useEffect、跨层数据用 useContext、可复用的逻辑封装成自定义 Hook。牢记「顶层调用」规则,就能避开绝大多数坑。