React FAQ
对 react 的理解
题目
谈谈对 react 的理解
参考答案
详见 React 基础
react 组件之间如何通信
题目
React 组件之间如何通信
参考答案
父子组件通信可以通过下面 3 种方法通信
- 父组件给子组件传递 props 数据
- 子组件回调父组件传的方法
- 父组件通过 ref 获得子组件的引用,调用子组件的方法
兄弟组件可以通过 2 种方法通信
- 事件总线
- 状态管理工具(redux、mobx)
setState 原理
题目
setState 原理,它是如何实现状态更改的?
参考答案
详见 setState
react 的 context
题目
介绍一下 react 的 context
参考答案
详见 context
react 列表的 key
题目
react 的列表中为什么需要 key 属性?
key 属性如果赋值为数组的 index,会有什么问题吗?
参考答案
不加 key 或者 key 使用 index 赋值,都会列表变动后导致 React 无法辨别 item 前后的对应关系。
这可能带来两种问题:
- 性能损耗,因为无法正确识别列表中元素变化,可能做一些多余的更新属性的操作,造成性能损失。
- 非受控的表单的值不符合预期,因为无法正确识别列表中元素变化,对于非受控的表单,React 无法知道表单的变化,也就没法正确地更新其值。
react diff 算法
题目
讲一下 react 的 diff 算法
参考答案
React 的 diff 算法基于两个假设:
1、不同元素的类型会产生不同的树
2、开发者可以通过 key prop 标识一个元素在不同的渲染下可以保持稳定(及相同 key 的元素不需要更新整个元素,不同的 key 需要更新整个元素)
基于上述假设,React 认为在视图更新前后,如果两个节点的 Tag 名(对于原生标签)或者组件(对于自定义组件)相同,则它们是同一个节点,而且如果是不同的根节点,那么子节点也不需要对比,直接用新树替换掉旧的树即可。
对比的过程
1、当一个组件触发更新(setState),则 React 会 diff 以这个组件为根节点的整个虚拟 DOM 树,对比更新后的虚拟 DOM 和更新前的虚拟 DOM。
2、如果两个节点不同,则用新的节点替换掉旧的节点;如果两个节点相同(假设为 oldNode 和 newNode),则对比它们的属性、innerText 和子节点。
3、如果 oldNode 有某个子节点 someOldChild,而 newNode 没有这个子节点(即 newNode 没有一个子节点,和 someOldNode 有相同的 tag 名、自定义组件引用或者 key 值),则删掉 someOldChild;如果 newNode 有某个子节点 somNewChild,而 oldNode 没有,则添加 someNewChild;如果 oldNode 和 newNode 都有某个节点 someChild,则将其移动到正确的位置,并递归地进行对比工作,即以 someChild 为根节点对比新旧两棵虚拟 DOM 树。
从上面的对比过程可以看出,React 的 diff(其实 Vue 也是一样)并不会考虑到节点跨层移动的情况,因此有些观点认为 React 的对比过程可以描述为“按层比较”(level by level)。
react 的 hook
题目
react hook 的好处和限制是什么?
参考答案
hook 是 react 提供的一类 API,它给函数式组件增加状态和逻辑,并将功能代码聚合在一起。
hook 好处是能够让不同功能代码更聚合,更好维护。
限制是:
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。(还有一个地方可以调用 Hook —— 就是自定义的 Hook 中)
题目
说一下常用的 hook 的使用方法和应用场景
参考答案
useState
在函数式组件中添加状态需要使用useState这个hook
useEffect
在函数式组件中添加一些有副作用的操作(如网络请求、操作DOM、绑定事件回调),需要用到useEffect这个hook。
useMemo
useMemo的作用类似于Vue中的computed,会缓存计算结果,只有当依赖的数据改变时候,才会执行其中的计算方法。
useCallback
一般用于生成事件处理函数,防止每次生成新的函数,导致不必要的子组件重新render。场景是计算属性和注册回调。
useRef
useRef可以用来实现获取一个原生DOM或者子组件实例的引用,本质上,它可以用来保存任何可变值。
题目
useState 和 userRef 怎么用?useState 如何实现的?
参考答案
useState 可以给函数式组件添加状态
useState 函数接收一个参数作为初始值
useState 返回值为一个数组,数组第一个元素是状态变量,第二个元素是设置该状态的方法
调用设置状态的方法,会同步触发 diff 和更新操作
useRef 可以用来实现获取一个原生 DOM 或者子组件实例的引用,本质上,它可以用来保存任何可变值。
useRef 接收一个参数作为初始值。
useRef 返回一个对象,这个对象包含一个 current 属性,这个 current 属性会被初始化为初始值,并且可以更改。
每次调用渲染函数,都返回同一个对象。
useState 工作的过程如下:
首次调用函数组件,执行 useState,React 获取到当前正在遍历的节点,创建_state 对象挂载到节点上,根据调用 useState 的顺序,在数组中加入状态,并根据传入的初始值初始化状态。
当调用状态的 set 方法时候,React 修改_state 上面的状态,并更新相应的组件。
后面再调用函数组件时候,useState 找到对应的节点的_state,并根据 useState 的调用顺序,找到修改后的状态返回,这样组件就可以拿到正确的状态了。