1. useState
这个钩子是不可避免的。
当你需要跨重新渲染持久化的动态状态时,这就是你要找的。
const [count, setCount] = useState(0);
2. useReducer
当你的状态变得复杂(例如多个相关字段)时,useReducer 就成了你的好帮手。
它也非常适合与 useContext 配合使用,让你可以通过简单的 dispatch 调用更新状态。
const reducer = (state, action) => {
switch (action.type) {
case "addTodo":
return { todos: [...state.todos, action.todo] };
case "removeTodo":
// TODO: Implement logic
case "toggleTodo":
// TODO: Implement logic
default:
return state;
}
};
const [state, dispatch] = useReducer(reducer, { todos: [] });
3. useContext
当属性穿透失控时,这个钩子拯救了局面。
Prop drilling = 将 props 通过多层传递,只是为了到达一个深层嵌套的组件。
使用 useContext 来:
- 在小型应用中管理全局状态
- 共享数据,如认证用户
- 避免手动到处传递 props
const UserContext = createContext();
function App() {
const user = useUser();
return (
<UserContext.Provider value={user}>
{/* ... */}
</UserContext.Provider>
);
}
function Profile() {
const user = useContext(UserContext);
return <div>Hello {user.name}</div>;
}
4. useEffect
99%的情况下你应该避免这样做。 (参见: 你可能不需要一个 Effect)
但在现实生活中,你仍然需要它——尤其是用于与外部系统同步等事情。
function useSetDocumentTitle(title: string) {
useEffect(() => {
document.title = title;
}, [title]);
}
5. useRef
你永远不应该在 React 中直接操作 DOM。
但当你必须使用时(例如 canvas、图表库等),useRef 是安全的方式。
function ExampleChart({ data }) {
const canvasRef = useRef();
useEffect(() => {
const ctx = canvasRef.current;
if (!ctx) return;
const chart = new Chart(ctx, {
type: "bar",
data: {
labels: data.map((row) => row.year),
datasets: [{ label: "Likes", data: data.map((r) => r.count) }],
},
});
return () => chart.destroy();
}, []);
return <canvas ref={canvasRef} />;
}
6. useMemo / useCallback
这些可能会因为新的 React 编译器而变为可选的——但目前它们仍然很有用。
当 useMemo 时:
- 昂贵的计算不应该在每次渲染时重新运行
- 你正在记忆一个非原始依赖项
- 你正在向 memo 包装的组件传递值
使用 useCallback 来缓存函数
const total = useMemo(() => computeTotal(items), [items]);
const handleClick = useCallback(() => {
console.log("clicked");
}, []);
7. useLayoutEffect
这一种比较少见但很有用。
它在渲染前运行,这有助于防止布局偏移或界面闪烁。
查看实际示例:不再有界面闪烁
如果你对 React 组件的生命周期不熟悉,可以查看我的文章 ✨3 分钟了解 React 生命周期 😉.
useLayoutEffect(() => {
const height = ref.current.offsetHeight;
setHeight(height);
}, []);
8. useSWR / useQuery
SWR 和 TanStack Query 正在成为 React 中获取数据的标准。
SWR 更简单,适用于大多数用例,而 TanStack Query 更复杂且强大。
SWR
const fetcher = (url) => fetch(url).then((res) => res.json());
const { data, error, isLoading } = useSWR("/api/user", fetcher);
TanStack Query
const getUser = () => fetch("/api/user").then((res) => res.json());
const { data, isLoading } = useQuery({
queryKey: ["user"],
queryFn: getUser,
});
9. useSelector, useDispatch 或其他状态钩子
有时候 React 的内置状态工具还不够用。
然后你会看到这些钩子依赖于状态库:
- useSelector, useDispatch from react-redux
- useAtom from Jotai
- etc.
10. useNavigate
React Router 非常流行。
所以它的导航钩子也是这样:
const navigate = useNavigate();
navigate("/profile");
11. useBoolean
我最喜欢的钩子之一。
它让管理布尔状态非常简洁。
const { value: isOpen, setFalse, toggle } = useBoolean();
return (
<>
<button onClick={toggle}>
{isOpen ? "Close form" : "Open form"}
</button>
<Dialog isOpen={isOpen} onClose={setFalse}>
{/* Form content */}
</Dialog>
</>
);
12. useCopyToClipboard
非常适合"复制"按钮。
const [copiedText, copy] = useCopyToClipboard();
const handleCopy = () => {
copy("Hello World").catch(err => {
errorToast("Failed to copy!", err);
});
};
return (
<>
<button onClick={handleCopy}>Copy</button>
{copiedText && <span>Copied!</span>}
</>
);
13. usePrevious
用来比较当前值和前一个值。
非常适合根据变化触发操作。
const prevCount = usePrevious(count);
const didIncrease = prevCount !== undefined && count > prevCount;
useEffect(() => {
if (didIncrease) {
console.log("Count increased!");
}
}, [didIncrease]);
14. useDebounce
对请求进行防抖,避免不必要的 API 调用。
const [query, setQuery] = useState("");
const [debouncedQuery] = useDebounce(query, 500);
const { data } = useSWR(`/api/search?q=${debouncedQuery}`, fetcher);
return (
<>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
{/* TODO: Show data */}
</>
);
15. useMediaQuery
想根据屏幕尺寸渲染不同内容?这个钩子就是为你准备的。
const isMobile = useMediaQuery("(max-width: 768px)");
return <div>{isMobile ? "Mobile View" : "Desktop View"}</div>;
16. useResizeObserver
适用于响应式布局或像 react-window 这样的库。
const ref = useRef(null);
const { width = 0, height = 0 } = useResizeObserver({ ref });
return (
<div ref={ref}>
Width: {width}, Height: {height}
</div>
);
17. useLocalStorage
轻松存储和同步 localStorage 中的数据。
const [theme, setTheme] = useLocalStorage("theme", "light");
return (
<button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
Switch to {theme === "light" ? "dark" : "light"}
</button>
);