我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

本文描述的问题依然是react项目,无论是父组件还是子组件,只有当项目内的state变量或者传入组件的props发生改变的时候,才应该触发组件的渲染。那么,上一篇文章里面,使用memo解决了多次渲染的问题,那么本文中则致力于解决相反的问题,即变量发生改变了,却并不会触发渲染的问题。据说本文所描述现象的根本原因是react渲染使用浅比较的原因所致,复杂类型就会被检测为没有变化。

苏南大叔:react项目,解决object类型状态改变但不及时触发渲染的问题 - react-state-object
react项目,解决object类型状态改变但不及时触发渲染的问题(图7-1)

大家好,这里是苏南大叔的程序如此灵动博客,这里记录苏南大叔和计算机代码的故事。本文描述触发渲染的变量特殊类型的问题,对于js编程语言来说,这些特殊类型的变量都是object类型。测试环境:create-react-app@5.0.1react@18.2.0react-dom@18.2.0node@16.14.2

现象描述

本文用例是基于如下文章链接的:

默认情况下来说,状态变量的改变,就会引起组件的渲染。还是以上面的例子为例,仅仅换个变量类型。react项目是基于函数式组件的。父组件有三个状态值(在react tools里面称之为hook值),作为本文的关键因素。分别是:

变量   数据说明    渲染
data1一个普通类型的数据整形、字符串会引起渲染
data2一个数组类型的数据js会认为数组是个object不一定会引起渲染
data3一个object类型的数据就是大家想的那个object类型不一定会引起渲染

这里有关array会被判断为object的事情,请参考文章:

作为一个父组件的state值(子组件的props值),修改的时候,按理说是一定会引起渲染动作的。但是object(包括array),修改其数据的时候,因为并不会引发变量内存地址的变化,所以并不会引发渲染。如果触发了内存地址的变化(比如一个新的变量地址)就会触发渲染动作。因此,分别有两种方式对data2data3进行修改。

变量   数据修改方式 渲染
data2一个数组类型的数据arr.push(xxx)不会引起渲染
data2一个数组类型的数据[...arr,xxx]会引起渲染
data3一个object类型的数据obj.prop = xxx不会引起渲染
data3一个object类型的数据{...obj,...{prop:xxx}}会引起渲染

苏南大叔:react项目,解决object类型状态改变但不及时触发渲染的问题 - 状态变量定义
react项目,解决object类型状态改变但不及时触发渲染的问题(图7-2)

测试代码

测试代码,以父组件触发渲染为例,进行演示。对应的数据,以props的形式传入子组件的时候,存在一致的是否渲染的关系。

app.js代码:

import './App.css';
import React, { useState } from 'react';

function App() {
  // 基本状态值三个
  const [data1, set_data1] = useState(0)
  const [data2, set_data2] = useState(["sunan"])
  const [data3, set_data3] = useState({ "name": "苏南大叔" })
  // 改变状态的办法,有两个对照组
  function _set_data2() {
    data2.push(data1 + ",传统方式");
    set_data2(data2);
  }
  function __set_data2() {
    //set_data2([...data2, data1 + ",三个点方式"]);
    set_data2(["苏南大叔,定义了一个新数组"]);
  }
  function _set_data3() {
    data3.name = data3.name + ",传统方式" + data1;
    set_data3(data3);
  }
  function __set_data3() {
    // set_data3({ ...data3, ...{ "name": data3.name + ",三个点方式" + data1 } });
    set_data3({"name":"苏南大叔,定义了一个新对象"});
  }
  // 组件调用
  return (
    <>
      {
        console.log("重新渲染了", Date.now())
      }
      <div className="App">
        <table>
          <tbody>
            <tr>
              <td>
                <button onClick={() => set_data1((val) => val + 1)}>data1永远会触发渲染,可以点此看渲染效果</button>
              </td>
              <td>{data1}</td>
            </tr>
            <tr>
              <td>
                <button onClick={_set_data2.bind()}>data2传统方式修改数据,不渲染</button>
                <button onClick={__set_data2.bind()}>data2三个点方式改数据,会渲染</button>
              </td>
              <td>{data2}</td>
            </tr>
            <tr>
              <td>
                <button onClick={_set_data3.bind()}>data3传统方式修改数据,不渲染</button>
                <button onClick={__set_data3.bind()}>data3三个点方式改数据,会渲染</button>
              </td>
              <td>{data3.name}</td>
            </tr>
          </tbody>
        </table>
      </div>
    </>
  );
}
export default App;

苏南大叔:react项目,解决object类型状态改变但不及时触发渲染的问题 - 状态函数调用
react项目,解决object类型状态改变但不及时触发渲染的问题(图7-3)

对于代码里面的useState的说明,请参考:

执行效果

界面上共三组按钮,

  • 第一组(个)是用于改变状态变量data1的,因为改变的是个普通的类型的值。所以常规更改即可触发渲染。
  • 第二组是用于改变状态数组变量data2的,传统的.push()方式,并不能触发渲染。而改用...方式则触发了渲染。
  • 第三组是用于改变状态数组变量data3的,传统的obj.xxx方式,并不能触发渲染。而改用...方式则触发了渲染。

苏南大叔:react项目,解决object类型状态改变但不及时触发渲染的问题 - 修改数据的方式
react项目,解决object类型状态改变但不及时触发渲染的问题(图7-4)

没有触发渲染的按钮动作,也实际上修改了变量的值,只不过没有被展示出来而已。

苏南大叔:react项目,解决object类型状态改变但不及时触发渲染的问题 - array-push-state
react项目,解决object类型状态改变但不及时触发渲染的问题(图7-5)

不能被触发渲染的动作

实际上改变了值,但是就是不会被触发渲染,因为内置地址没有发生改变。

function _set_data2() {
  data2.push(data1 + ",传统方式");
  set_data2(data2);
}
function _set_data3() {
  data3.name = data3.name + ",传统方式" + data1;
  set_data3(data3);
}

这两个动作的共同特点,就是直接修改的原object的值。(arrayjs类型识别的时候,也是object

苏南大叔:react项目,解决object类型状态改变但不及时触发渲染的问题 - object-xxx-state
react项目,解决object类型状态改变但不及时触发渲染的问题(图7-6)

被触发渲染的动作

下面的函数动作,触发了实际的渲染动作。

function __set_data2() {
  set_data2([...data2, data1 + ",三个点方式"]);
}
function __set_data3() {
  set_data3({ ...data3, ...{ "name": data3.name + ",三个点方式" + data1 } });
}

当然,也不是说只有三个点才会触发渲染,只是这种三个点的方式【表象】,实际上创建了新的变量【重点】,改变了内存地址。参考文章:

比如下面这种普通的重新定义变量的方式,也是可以触发渲染的。

function __set_data2() {
  set_data2(["苏南大叔,定义了一个新数组"]);
}
function __set_data3() {
  set_data3({"name":"苏南大叔,定义了一个新对象"});
}

苏南大叔:react项目,解决object类型状态改变但不及时触发渲染的问题 - object触发渲染
react项目,解决object类型状态改变但不及时触发渲染的问题(图7-7)

相关链接

总结

更多react经验文章,请点击下面的链接:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   react