React Hook

249 阅读3分钟

一、出现原因

在 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);
  // ...
}