一、出现原因
在 Hooks 出现之前,类组件和函数组件的分工一般是这样的:
- 类组件提供了完整的状态管理和生命周期控制,通常用来承接复杂的业务逻辑
- 函数组件则是纯粹的从数据到视图的映射,对状态毫无感知
二、本质
在不编写类组件的情况下也能使用state来构建组件自身数据,Hook组件就是个类似于无状态组件的函数。
Hook只是一种“工具”,这种工具可以让我们在无状态组件中使用到state以及生命周期等特性。
三、useState
const [state, setState] = useState(initialValue);
- state 就是一个状态变量
- setState 是一个用于修改状态的 Setter 函数
- initialValue 则是状态的初始值
四、useEffect
1、使用
useEffect(effectFn, deps)
// 例子
useEffect(() => {
const intervalId = setInterval(doSomething(), 1000);
return () => clearInterval(intervalId);
});
- effectFn:执行某些可能具有副作用的 Effect 函数(例如数据获取、设置 / 销毁定时器等),它可以返回一个清理函数(Cleanup)
- deps:依赖数组,用来控制是否应该触发 Effect,从而能够减少不必要的计算,优化性能。(只要依赖数组中的每一项与上一次渲染相比都没有改变,那么就跳过本次 Effect 的执行。一般来说,所使用到的 prop 或者 state 都应该被添加到 deps 数组里面去。)
- 每个 Effect 必然在渲染之后执行,因此不会阻塞渲染,提高了性能
- 在运行每个 Effect 之前,运行前一次渲染的 Effect Cleanup 函数
- 当组件销毁时,运行最后一次 Effect 的 Cleanup 函数
2、useEffect 钩子与之前类组件的生命周期相比
- 将初次渲染(componentDidMount)、重渲染(componentDidUpdate)和销毁(componentDidUnmount)三个阶段的逻辑用一个统一的 API 去解决
- 把相关的逻辑都放到一个 Effect 里面(例如 setInterval 和 clearInterval),更突出逻辑的内聚性
五、自定义 Hook
自定义 Hook 本质上只是把调用内置 Hook 的过程封装成一个个可以复用的函数,并不影响 Hook 链表的生成和读取。
六、useCallback 与 useMemo(为了解决deps传入函数时每次渲染都有改变的问题)
1、useCallback
const memoizedCallback = useCallback(callback, deps);
- callback:需要记忆的函数
- deps:依赖数组,相当于缓存中的键(Key),如果键没有改变,那么就直接返回缓存中的函数,并且确保是引用相同的函数。在大多数情况下,我们都是传入空数组 [] 作为 deps 参数,这样 useCallback 返回的就始终是同一个函数,永远不会更新。
2、useMemo
const memoizedCallback = useMemo(() => fn, deps);
3、useCallback 和 useMemo 的关系
- useCallback 主要是为了解决函数的” 引用相等 “问题,而 useMemo能够同时胜任引用相等和节约计算的任务。
- useMemo 的功能是 useCallback 的超集。与 useCallback 只能缓存函数相比,useMemo 可以缓存任何类型的值。
七、useReducer 和 useContext
1、useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
- reducer:形式跟 Redux 中的 Reducer 函数完全一致,即 (state, action) => newState
- initialArg:状态的初始值
- init: 一个可选的用于懒初始化(Lazy Initialization)的函数,这个函数返回初始化后的状态
// Reducer 函数
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
</>
);
}
2、useContext
// 在某个文件中定义 MyContext
const MyContext = React.createContext('hello');
// 在函数式组件中获取 Context
function Component() {
const value = useContext(MyContext);
// ...
}