前端高频面试题---React篇(三)

214 阅读8分钟

1. react中setState是同步还是异步?

在React中,setState是一个异步函数,这意味着它不会立即更新组件的状态。当我们在组件中调用setState时,React会将这个操作放入一个队列中,然后在适当的时间点执行。

这种异步行为有几个重要的原因:

  1. 提高性能:React使用批量更新的机制,将多个setState操作合并为一个批处理,以减少重绘的次数,提高性能。这意味着在setState被调用之后,状态可能不会立即更新,而是等待React执行批处理操作。
  2. 避免不必要的重绘:如果一个组件的状态发生了多次变化,React会将这些变化合并到一起,并只触发一次重绘。这样可以避免不必要的重绘,提高性能。
  3. 异步操作:有时候我们需要等待异步操作完成后再更新状态,例如从服务器获取数据并更新组件的状态。在这种情况下,使用异步函数可以更好地处理异步操作,避免阻塞主线程。

由于setState是异步的,因此我们需要注意一些问题:

  1. 在setState之后的回调函数中,新状态可能还未被更新。因此,我们不能依赖新状态进行操作。
  2. 如果连续调用多个setState,React会将它们合并为一个批处理。这意味着在第一个setState被执行之后,第二个setState可能还未被应用,因此我们需要注意它们的执行顺序。
  3. 在异步操作中使用setState时,我们需要使用回调函数或者Promise来确保在异步操作完成后更新状态。

总的来说,React中的setState是异步的,这是为了提高性能和避免不必要的重绘。我们需要了解这种异步行为,并相应地调整我们的代码以确保正确的状态管理和提高性能。

2. 为什么建议传递给setState的参数是一个callback而不是一个对象?

建议将setState的参数传递为回调函数而不是对象的原因有以下几点:

  1. 批量更新:当setState接收到一个对象时,React会尝试一次性更新所有状态。这可能导致在渲染过程中多次触发组件,并可能引起性能问题。而传递一个回调函数可以确保状态在组件渲染后更新,从而减少不必要的重渲染。
  2. 异步性质:setState是异步的,这意味着在调用setState后,状态并不会立即更新。如果你传递一个对象给setState,并在回调函数中尝试访问新状态,那么你可能会遇到一些问题,因为新状态可能还没有被应用。而如果你传递一个回调函数,你可以确保在状态更新后执行某些操作。
  3. 并发模式:React 18引入了并发模式(Concurrent Mode),在这种模式下,React可以在用户界面(UI)卡顿的时候暂停渲染过程。如果setState接收到的参数是一个对象,那么React可能会在回调函数执行之前就暂停渲染过程,这可能会导致一些预期之外的行为。而如果setState接收到的是回调函数,那么React可以在回调函数执行之后再暂停渲染过程,从而避免这种情况。

然而,这并不是说永远不应该将一个对象传递给setState。在一些情况下,如当你有多个状态需要同时更新时,或者当你有多个状态值需要基于相同的源数据时,使用一个对象来传递状态可能是更方便的。但在大多数情况下,使用回调函数是更好的选择。

3. 为什么要设计 React hook,它解决了什么问题?

React Hook 是 React 16.8 版本新增的特性,它解决了在使用 React 组件时遇到的一些问题。具体来说,React Hook 允许在函数组件中更方便地使用状态和其他 React 特性,而无需使用类组件。

具体来说,React Hook 解决了以下问题:

  1. 状态管理:在函数组件中,无法像在类组件中使用 this.state 来管理状态。而使用 React Hook,可以很容易地使用 useState 来添加状态管理功能。
  2. 副作用处理:在函数组件中,无法像在类组件中使用 this.componentDidMountthis.componentDidUpdatethis.componentWillUnmount 来处理生命周期方法和副作用。而使用 React Hook,可以很容易地使用 useEffect 来处理这些副作用。
  3. 逻辑复用:在函数组件中,如果有很多组件具有相似的状态逻辑,需要重复编写相同的代码。而使用 React Hook,可以很容易地将这些逻辑封装成自定义 Hook,并在多个组件中复用。
  4. 组件通信:在函数组件中,父组件无法直接将状态传递给子组件。而使用 React Hook,可以通过 useContextuseReducer 等 Hook 来实现上下文通信,使得状态传递更加方便。

总之,React Hook 的设计是为了解决函数组件在使用过程中遇到的一些问题,使得函数组件更加方便、易于维护和复用。

4. react中的hooks为什么不能写在判断代码块中?

React Hooks 不能写在条件(判断)语句中的主要原因是为了保持每次组件渲染的一致性。

当你在条件语句中使用 React Hook 时,会导致以下问题:

  1. 每次渲染不一致:在条件语句中,Hook 的调用时机可能因条件判断而改变,导致每次渲染产生不同的结果。这破坏了 React 的不变性原则,使得组件的状态管理变得复杂和不可预测。
  2. 无效的依赖收集:在 React 重新渲染时,会根据依赖关系进行有效的重绘。然而,在条件语句中,Hook 的调用时机可能因条件判断而改变,导致无效的依赖收集,从而影响性能。

为了保持组件状态的一致性和性能优化,React 官方建议将 Hook 放在组件的顶层,避免在条件语句中使用 Hook。如果你需要在条件渲染中使用状态,可以考虑使用其他方式,例如使用 useMemouseState 的返回值进行条件判断。

5. 知道 react 的 fiber,谈谈它的作用和实现?

React Fiber 是 React 团队开发的一种新的调度算法,用于提高 React 应用的性能和响应能力。它是在 React 15 及之前的版本所使用的 "Stack Reconciliation" 调度算法的基础上进行改进的。

React Fiber 的作用:

  1. 碎片化更新过程:React Fiber 将更新过程划分为多个优先级较低的小任务,利用浏览器的空闲时间进行调度和执行。这种方式可以更好地控制任务的优先级和执行顺序,从而提高了 React 的性能和响应能力。
  2. 阶段分解:React Fiber 将组件树的调度过程分解为多个阶段,每个阶段可以中断和恢复。这样可以让浏览器在执行任务时具有更高的灵活性,以提供更好的用户体验。
  3. 优先级调度:React Fiber 通过优先级调度算法来确定任务的执行顺序,高优先级的任务会先执行。这种机制使得 React Fiber 能够更好地控制任务的执行顺序,进一步提高性能和响应能力。
  4. 异步任务:React Fiber 支持异步任务,通过调用 requestIdleCallback API,在浏览器空闲的时候执行异步任务,从而不阻塞主线程,使得应用能够更好地利用浏览器资源。
  5. 链表与队列:React Fiber 中的数据结构是 Fiber,每个 Fiber 代表一个工作单元。在 React Fiber 中,组件树被转化为链表结构,每个节点对应一个 Fiber。同时,每个 Fiber 都有两个队列,一个用于更新过程,一个用于异步任务。这种结构使得 React Fiber 能够更好地管理任务和调度过程。

React Fiber 的实现:

React Fiber 是对 React 核心算法(即调和过程)的重写。它从架构角度来看,是对 React 的底层调度算法进行优化。在编码方面,React Fiber 是 React 内部所定义的一种数据结构,它是 Fiber 树结构的节点单位。每个 Fiber 代表一个工作单元,通过维护每个分片的优先级和数据结构,实现了更好的任务调度和执行控制。

总之,React Fiber 是 React 团队为了提高应用性能和响应能力而开发的一种新的调度算法。它通过对更新过程进行碎片化和阶段分解,以及引入优先级调度和异步任务等机制,使得 React 应用能够更好地利用浏览器资源,提供更流畅的用户体验。同时,React Fiber 的实现需要对 React 的底层调度算法进行优化和重写,需要具备较高的技术能力和对 React 原理的深入理解。