全面解析 React Hook
·
文章目录
一、什么是 React Hook?
React Hook 是 React 16.8 版本中引入的一项革命性特性。它是一系列特殊的函数,允许你在 函数组件 中使用 React 的特性,如状态(state)、生命周期(lifecycle)、上下文(context)等。
简单来说,Hook 让你“钩入” React 的功能。
二、为什么需要 Hook? (解决了什么问题?)
在 Hook 出现之前,函数组件只能做简单的 UI 渲染(无状态组件),如果要使用状态或生命周期,就必须编写类组件(class component)。但这带来了几个问题:
- 状态逻辑难以复用: 在组件之间复用状态逻辑很麻烦,需要用到 render props 或高阶组件等复杂模式,导致“嵌套地狱”。
- 复杂组件难以理解: 一个类组件的生命周期函数(如
componentDidMount)中可能包含多个不相关的逻辑(如设置订阅、获取数据),而相关的逻辑又被拆分到不同的生命周期中(如清理工作在componentWillUnmount中),导致代码混乱。 - 令人困惑的
this: 类组件中的this指向问题对于初学者和资深开发者来说都很令人困惑。
Hook 的出现,使得函数组件的能力得到了极大的增强,现在完全可以替代绝大多数类组件。
三、常用的内置 Hook (Built-in Hooks)
1. 基础 Hook (最核心的三个)
-
useState(状态钩子)- 作用: 在函数组件中添加和管理内部状态(state)。
- 示例:
import { useState } from 'react'; function Counter() { // count 是当前的状态值,setCount 是更新它的函数 const [count, setCount] = useState(0); return ( <div> <p>你点击了 {count} 次</p> <button onClick={() => setCount(count + 1)}> 点击我 </button> </div> ); }
-
useEffect(副作用钩子)- 作用: 在函数组件中执行副作用操作。它融合了类组件中的
componentDidMount,componentDidUpdate, 和componentWillUnmount三个生命周期的功能。 - 常见副作用: 数据获取、设置订阅、手动更改 DOM。
- 示例:
import { useState, useEffect } from 'react'; function Example() { const [data, setData] = useState(null); // useEffect 接收一个函数(“effect”) useEffect(() => { // 此处的代码会在组件渲染后执行(相当于 componentDidMount + componentDidUpdate) fetch('https://api.example.com/data') .then(response => response.json()) .then(data => setData(data)); // 可选的清理函数(相当于 componentWillUnmount) return () => { // 在这里执行清理工作,比如取消网络请求、清除定时器、移除事件监听器等 }; }, []); // 依赖项数组。如果为空数组 [],则 effect 只会在组件挂载后执行一次 return <div>{/* 渲染数据 */}</div>; }
- 作用: 在函数组件中执行副作用操作。它融合了类组件中的
-
useContext(上下文钩子)- 作用: 无需组件嵌套即可订阅 React 的 Context,从而在整个应用层级共享数据。
- 示例:
import { createContext, useContext } from 'react'; const ThemeContext = createContext('light'); function ThemedButton() { // 直接获取 Context 的值 const theme = useContext(ThemeContext); return <button className={theme}>我的主题是 {theme}</button>; }
2. 额外的常用 Hook
useReducer:useState的替代方案,适用于复杂的状态逻辑,类似于 Redux 的工作流程。useCallback: 缓存函数,避免非必要的重渲染,用于性能优化。useMemo: 缓存计算结果,避免每次渲染都进行重复计算,用于性能优化。useRef: 返回一个可变的 ref 对象,其.current属性被初始化为传入的参数。常用作访问 DOM 节点或存储任意可变值。useLayoutEffect: 与useEffect类似,但会在所有 DOM 变更之后同步调用 effect。主要用于读取 DOM 布局并同步触发重渲染。
四、Hook 的使用规则 (两条铁律)
使用 Hook 必须遵守以下规则:
- 只在最顶层使用 Hook: 不要在循环、条件判断或嵌套函数中调用 Hook。必须确保 Hook 在每次渲染时都以完全相同的顺序被调用。这是 React 能正确管理 Hook 状态的保证。
- 只在 React 函数中调用 Hook: 只能在 React 的函数组件或自定义 Hook 中调用 Hook。不要在普通的 JavaScript 函数中调用。
ESLint 的 eslint-plugin-react-hooks 插件会自动强制执行这些规则。
五、自定义 Hook (Custom Hooks)
这是 Hook 最强大的特性之一:自定义 Hook 可以将组件逻辑提取到可重用的函数中。
自定义 Hook 是一个以 use 开头的 JavaScript 函数,它内部可以调用其他的 Hook。
示例:自定义一个获取数据的 Hook
// useFetch.js (自定义 Hook)
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then(response => response.json())
.then(data => {
setData(data);
setLoading(false);
})
.catch(error => {
setError(error);
setLoading(false);
});
}, [url]); // 当 url 变化时,会再次执行
return { data, loading, error };
}
// MyComponent.js (使用自定义 Hook 的组件)
function MyComponent() {
// 像使用内置 Hook 一样使用它
const { data, loading, error } = useFetch('https://api.example.com/data');
if (loading) return <div>加载中...</div>;
if (error) return <div>出错啦!</div>;
return <div>{JSON.stringify(data)}</div>;
}
六、总结
| 特性 | 类组件 | 函数组件 + Hook |
|---|---|---|
| 状态 | this.state 和 this.setState() |
useState, useReducer |
| 生命周期 | componentDidMount, componentDidUpdate 等 |
useEffect |
| 代码复用 | 高阶组件、Render Props | 自定义 Hook (更简单清晰) |
| 代码组织 | 逻辑分散在不同的生命周期中 | 相关逻辑组织在同一个 Hook 中 |
| 学习成本 | 需要理解 this 和生命周期 |
更接近于纯 JavaScript 函数 |
结论:Hook 是 React 的未来。对于新项目和新组件,强烈推荐使用函数组件配合 Hook 进行开发。
更多推荐


所有评论(0)