什么是 React Hook

Hook 是 React 16.8 的新增特性

  • 允许开发者在函数组件里使用 React state 及生命周期等特性的函数
    • React 内置如 useState、useEffect 等 Hook
  • Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
    • 非强制按照生命周期划分,避免每个生命周期包含不相关的逻辑
  • 开发者可以在不编写 class 的情况下使用 state 以及其他的 React 特性
    • 无需考虑 this,无需考虑函数和 class 组件的区别和应用场景
    • 便于使用 Prepack 试验 component folding,使代码更易于优化
    • 拥抱函数式编程
  • Hook 和现有代码可以同时工作,渐进式地使用

什么是 State Hook

  • State Hook 允许开发者在 React 函数组件中添加 state 的 Hook
  • 这种 Hook 在 React 的原生实现是 useState,它是一种函数调用时保存变量的方式,它与 class 里面的 this.state 提供的功能完全相同
  • useState 的唯一参数是初始 state,支持数字、字符串、对象等类型
  • useState 方法的返回值分别是当前 state 以及更新 state 的函数,使用数组解构获取赋值
  • state 只在组件首次渲染时创建,下次重新渲染时,返回当前的 state
1
const [count, setCount] = useState(0);

什么是 Effect Hook

  • Effect Hook 允许开发者在函数组件中执行副作用操作,包括数据获取、设置订阅以及手动更改 React 组件中的 DOM。
  • 这种 Hook 在 React 的原生实现是 useEffect
  • useEfect 支持两个参数
    • 第一个参数为函数,默认在组件第一次渲染和每次更新、浏览器完成画面渲染之后执行,调用时机可以看作 class 组件的 componentDidMount、componentDidUpdate 生命周期 + 浏览器完成画面渲染时
    • 第二个参数为数组,只有数组元素发生变化时,才会调用第一个参数的函数
    • 当第二个参数为空数组 [] 时,第一个参数的函数仅会在组件挂载和卸载时执行
  • useEffect 可以返回一个函数,用于移除订阅等副作用,区别于 class 组件的 componetWillUnMount,React 会在执行当前 effect 之前对上一个 effect 进行清除
  • useEffect 可以直接访问 state 和 props
1
2
3
4
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});

useReducer 和 useState 区别

useReducer 是 useState 在某些场景下的替代方案,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。一个典型的应用场景就是计数器。

1
const [state, dispatch] = useReducer(reducer, initialArg, init);

useMemo 和 useCallback 区别

useMemo 返回一个 被记忆的 值,把值的“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算该 被记忆的 值。这种优化有助于避免在每次渲染时都进行高开销的计算。

useCallback 返回一个 被记忆的 回调函数。把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

useEffect 和 useLayoutEffect 区别

相同点:

  • 执行时机都是组件挂载或更新之后,浏览器执行绘制之前
  • 支持返回清除函数,函数执行时机是组件卸载之前

不同点:

  • useLayoutEffect 在所有 DOM 变更之后同步调用 effect,用来读取 DOM 布局并同步触发重渲染
  • useEffect 在所有 DOM 变更并且浏览器完成渲染后调用 effect
  • 服务端渲染组件引入 useLayoutEffect 会触发 React 告警

useRef 和 Refs 的区别

useRef 返回一个可变的 ref 对象,其 current 属性被初始化为传入的参数 (initialValue),返回的 ref 对象在组件的整个生命周期保持不变。
一个常见的用例便是命令式地访问子组件,例如将 input 元素的 ref 属性设置为 useRef 返回的 ref 对象,则可以通过该 ref 对象的 current 属性设置 input 元素是否聚焦。

1
2
3
4
5
6
7
8
9
10
11
12
13
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}

Refs 使用 React.createRef() 创建的,并通过 ref 属性附加到 React 元素,当 ref 被传递给 render 中的元素时,对节点的引用可以在 ref 的 current 属性中被访问。

1
2
3
4
5
6
7
8
9
class Component extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
render() {
return <div ref={this.myRef} />
}
}

区别:
useRef

  • 函数组件可用
  • 用途多样,useRef() 创建一个普通 Javascript 对象,每次渲染时返回同一个 ref 对象
    可以在其 current 属性保存任何可变值。
    Refs
  • class 组件或 HTML 元素,函数组件无实例,不可用
  • 用途单一,current 属性为实例的引用,根据节点的类型不同
    • 当 ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为 current 属性
    • 当 ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性

如何自定义 Hook

什么是自定义 Hook

自定义 Hook,是将组件逻辑提取到可重用的函数,它可以像 render props 和高阶组件来共享组件之间的状态逻辑,而不增加组件。

自定义 Hook 需要遵循哪些规则

  • 名称以 “use” 开头的函数,表示这是一个 Hook,函数内部可以调用其他的 Hook。
  • 参数和返回可以自定义,可以像使用函数一样在不同 Hook 间传递信息
  • 其中所有 state 和副作用完全隔离,每次调用 Hook,都会获取独立的 state

自定义 Hook 作用

将组件逻辑提取到可重用的函数中,简化代码逻辑

为什么不能在循环,条件或嵌套函数中调用 Hook?

React 通过 Hook 调用的顺序来将 state 与对应的 useState 相关联,所以需要确保 Hook 的调用顺序在多次渲染之间保持一致。如果想要有条件地执行一个 effect,可以将判断放到 Hook 的内部。

useDeferredValue 和 useTransition 的区别

  • useDeferredValue 本质上和内部实现与 useTransition 一样,都是标记成了延迟更新任务。
  • useTransition 是把更新任务变成了延迟更新任务,而 useDeferredValue 是产生一个新的值,这个值作为延时状态。(一个用来包装方法,一个用来包装值)