什么是 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 | useEffect(() => { |
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 | function TextInputWithFocusButton() { |
Refs 使用 React.createRef()
创建的,并通过 ref 属性附加到 React 元素,当 ref 被传递给 render 中的元素时,对节点的引用可以在 ref 的 current 属性中被访问。
1 | class Component extends React.Component { |
区别:
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 是产生一个新的值,这个值作为延时状态。(一个用来包装方法,一个用来包装值)