关于为什么函数式组件setState一个react组件会崩掉

2024-3-28 6,795 3/28

也是今天突然的回想到了这个问题,然后想通了;。。。

关于前面做低代码时,做远程组件的加载,拿到远程组件后,函数式组件的setState进去,渲染时会崩掉的问题。

原因在于setState是可以接收两种参数的,传入一个函数的话,他会执行后返回函数的执行结果,在去set一个新的状态,这个的话是react的基础问题了。

import React, { useEffect, useState } from "react";
import "./1.0-Component.css";
function Co(props) {
  const [count, setCount] = useState(1);
  useEffect(() => {
    setCount(2);
  }, []);
  console.log(props, "props");
  return (
    <p>
      {1}
      {props?.c}
    </p>
  );
}
function Demo() {
  const [Com, setCom] = useState();
  const handleClick = () => {
    // 第一种
    // setCom(Co)

    // 第二种
    // setCom(<Co />)

    // 第三种
    setCom(() => Co);
  };

  return (
    <button onClick={handleClick}>
      test
      {Com && <Com />}
    </button>
  );
}

export default Demo;

关键在于就是,我们拿到的组件,其实也就是一个已经经过babel编译过的render函数了,那么直接去set进去,就会执行这个函数,render函数返回的值是什么呢? 是组件节点的虚拟dom的对象,所以此时的state已经是一个虚拟dom的对象了,我们渲染的话直接渲染该对象即可,如果还是以jsx语法去进行渲染的话就会,崩掉,因为渲染的本质就是render方法的执行。

这里的虚拟dom对象的结构

$$typeof: Symbol(react.element)
key: null
props: {}
ref: null
type: ƒ Co(props)
_owner: null
_store: {validated: false}
_self: undefined
_source: {fileName: '/Users/linzai/WebstormProjects/codelearn/src/lesson-1/1.0-Component.jsx', lineNumber: 24, columnNumber: 12}
[[Prototype]]: Object

type属性只有我们自己定义的组件才有这个属性,并且可以通过执行虚拟dom的type方法进行render的,props 也通过方法参数进行一个传递。(这个拓展一下render方法做的事情也就是如何去生成虚拟dom的,emmm其实也只是去处理的keyt,ype,ref,props,还有就是递归的执行children里面的内容,diff的话,也是虚拟dom节点与fiber节点的key与type做对比而已)

 

第二种的话是set一个jsx的组件进去,但是与set一个函数的区别在于set一个函数对象,他会直接去执行,函数,然后返回,返回的不是组件本身了,而是基于真实dom构建的虚拟dom对象,也就是返回的对象是没有type这个属性的,但是我们去set一个jsx的话,emm他执行的其实是经过babel编辑后的产生的render函数,他们之间是有很大的差别的 。

后面的解决办法的话,我们去用第三种的方式去做一个执行即可,这样返回的也只是组件对应的render函数了,而不是虚拟dom的对象

当然也有其他的解决办法,就是直接set一个组件的话也是ok的,不过会在set的时候就已经执行了render函数了,返回的虚拟对象是可以直接使用的,因为渲染的本质也只是render的执行生成虚拟dom树而已。但是props如何传递呢???,第已反应是给虚拟对象赋值props属性,生成的虚拟dom确实由props这个key,但是是只读的,应该是在对象的可描述属性那一层对props进行重写了,,我觉得可以试试对象合并,这种没试过。

第三种是去执行生成的虚拟dom的type方法,也是ok的,不过这只是基于我们自己的组件才有这个方法。

 

问题的核心:jsx的本质是一个render函数,setState如果传入一个参数的话会去执行,然后将结果作为新的state(很基础的东西了),渲染的过程本就是render方法的执行,生成虚拟dom,基于新dom树与旧fiber树进行diff(为什么是旧fiber树,因为react实际上会维护两颗fiber树,一颗对应当前渲染的dom树,另一颗旧的就是上次diff完成后的dom树,其实这个理解还是很难的,,,)。崩掉的话是在render执行就g了。

 

- THE END -
3

共有 2 条评论

  1. Like

    涛神

  2. Like

    god