React 中的重新渲染

作者:梁瑞锋(晓玉)

缘起

React 重新渲染,指的是在类函数中,会重新执行 render 函数,类似 Flutter 中的 build 函数,函数组件中,会重新执行这个函数

React 组件在组件的状态 state 或者组件的属性 props 改变的时候,会重新渲染,条件简单,但是实际上稍不注意,会引起灾难性的重新渲染

类组件

为什么拿类组件先说,怎么说呢,更好理解?还有前几年比较流行的一些常见面试题

React 中的 setState 什么时候是同步的,什么时候是异步的

React setState 怎么获取最新的 state

以下代码的输出值是什么,页面展示是怎么变化的

  test = () => {
   
    // s1 = 1
    const {
    s1 } = this.state;
    this.setState({
    s1: s1 + 1});
    this.setState({
    s1: s1 + 1});
    this.setState({
    s1: s1 + 1});
    console.log(s1)
  };

  render() {
   
    return (
      <div>
        <button onClick={
   this.test}>按钮</button>
        <div>{
   this.state.s1}</div>
      </div>
    );
  }

看到这些类型的面试问题,熟悉 React 事务机制的你一定能答出来,毕竟不难嘛,哈?你不知道 React 的事务机制?百度|谷歌|360|搜狗|必应 React 事务机制

React 合成事件

React 组件触发的事件会被冒泡到 document(在 react v17 中是 react 挂载的节点,例如 document.querySelector(‘#app’)),然后 React 按照触发路径上收集事件回调,分发事件。

  • 这里是不是突发奇想,如果禁用了,在触发事件的节点,通过原生事件禁止事件冒泡,是不是 React 事件就没法触发了?确实是这样,没法冒泡了,React 都没法收集事件和分发事件了,注意这个冒泡不是 React 合成事件的冒泡。
  • 发散一下还能想到的另外一个点,React ,就算是在合成捕获阶段触发的事件,依旧在原生冒泡事件触发之后
reactEventCallback = () => {
   
  // s1 s2 s3 都是 1
  const {
    s1, s2, s3 } = this.state;
  this.setState({
    s1: s1 + 1 });
  this.setState({
    s2: s2 + 1 });
  this.setState({
    s3: s3 + 1 });
  console.log('after setState s1:', this.state.s1);
  // 这里依旧输出 1, 页面展示 2,页面仅重新渲染一次
};

<button
  onClick={
   this.reactEventCallback}
  onClickCapture={
   this.reactEventCallbackCapture}
>
  React Event
</button>
<div>
  S1: {
   s1} S2: {
   s2} S3: {
   s3}
</div>

定时器回调后触发 setState

定时器回调执行 setState 是同步的,可以在执行 setState 之后直接获取,最新的值,例如下面代码

timerCallback = () => {
   
  setTimeout(() => {
   
    // s1 s2 s3 都是 1
    const {
    s1, s2, s3 } = this.state;
    this.setState({
    s1: s1 + 1 });
    console.log('after setState s1:', this.state.s1);
    // 输出 2 页面渲染 3 次
    this.setState({
    s2: s2 + 1 });
    this.setState({
    s3: s3 + 1 });
  });
};

异步函数后调触发 setState

异步函数回调执行 setState 是同步的,可以在执行 setState 之后直接获取,最新的值,例如下面代码

asyncCallback = () => {
   
  Promise.resolve().then(() => {
   
    // s1 s2 s3 都是 1
    const {
    s1, s2, s3 } = this.state;
    this.setState({
    s1: s1 + 1 });
    console.log('after setState s1:', this.state.s1);
    // 输出 2 页面渲染 3 次
    this.setState({
    s2: s2 + 1 });
    this.setState({
    s3: s3 + 1 });
  });
};

原生事件触发

原生事件同样不受 React 事务机制影响,所以 setState 表现也是同步的

componentDidMount() {
   
  const btn1 = document.getElementById('native-event');
  btn1?.addEventListener('click', this.nativeCallback);
}

nativeCallback = () => {
   
  // s1 s2 s3 都是 1
  const {
    s1, s2, s3 } = this.state;
  this.setState({
    s1: s1 + 1 });
  console.log('after setState s1:', this.state.s1);
  // 输出 2 页面渲染 3 次
  this.setState({
    s2: s2 + 1 });
  this.setState({
    s3: s3 + 1 });
};


<button id="native-event">Native Event</button>

setState 修改不参与渲染的属性

setState 调用就会引起就会组件重新渲染,即使这个状态没有参与页面渲染,所以,请不要把非渲染属性放 state 里面,即使放了 state,也请不要通过 setState 去修改这个状态,直接调用 this.state.xxx = xxx 就好,这种不参与渲染的属性,直接挂在 this 上就好,参考下图

// s1 s2 s3 为渲染的属性,s4 非渲染属性
state = {
   
  s1: 1,
  s2: 1,
  s3: 1,
  s4: 1,
};

s5 = 1;

changeNotUsedState = () => {
   
  const {
    s4 } = this.state;
  this.setState({
    s4: s4 + 1 });
  // 页面会重新渲染

  // 页面不会重新渲染
  this.state.s4 = 2;
  this.s5 = 2;
};

<div>
  S1: {
   s1} S2: {
   s2} S3: {
   s3}
</div>;

只是调用 setState,页面会不会重新渲染

几种情况,分别是:

  • 直接调用 setState,无参数
  • setState,新 state 和老 state 完全一致,也就是同样的 state
sameState = () => {
   
  const {
    s1 } = this.state;
  this.setState({
    s1 });
  // 页面会重新渲染
};

noParams = () => {
   
  this.setState({
   });
  // 页面会重新渲染
};

这两种情况,处理起来和普通的修改状态的 setState 一致,都会引起重新渲染的

多次渲染的问题

为什么要提上面这些,仔细看,这里提到了很多次渲染的 3 次,比较契合我们日常写代码的,异步函数回调,毕竟在定时器回调或者给组件绑定原生事件(没事找事是吧?),挺少这么做的吧,但是异步回调就很多了,比如网络请求啥的,改变个 state 还是挺常见的,但是渲染多次,就是不行!不过利用 setState 实际上是传一个新对象合并机制,可以把变化的属性合并在新的对象里面,一次性提交全部变更,就不用调用多次 setState

asyncCallbackMerge = () => {
   
  Promise.resolve().then(<
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值