React 对state的理解(上)

本文详细介绍了React中复杂类组件与简单函数组件的区别,关键在于是否有状态(state)。状态驱动页面更新,且存在于组件实例上。通过实例展示了如何初始化和修改state,以及在处理事件时正确引用组件方法。同时,文章通过错误案例分析了this的指向问题,强调了在事件处理函数中正确绑定this以访问和修改state的重要性。
摘要由CSDN通过智能技术生成

如何定义复杂组件(类组件)与简单组件(函数组件)?

  • 是否具有状态(state)
引出问题,什么是状态?

举个例子,今天考试,考砸了,因为我状态不好,是状态影响了我的行为
组件中的状态驱动了页面更新,换句话说,组件状态中存着数据,数据的改变,驱动页面的更新。
在这里插入图片描述

这里要了解,state状态是谁身上的状态?
:state状态是组件实例对象身上的状态,不是组件类本身身上的,而是由这个类缔造的实例身上的。
了解一下实例对象:
c1就是Car类的实例对象。

<script>
  class Car {
    constructor(name, price) {
      this.name = name;
      this.price = price;
    }
  }
  const c1 = new Car("宝马", 100);
  console.log("c1", c1);
</script>

类本身身上:
在这里插入图片描述

重点:(class)组件实例上三大属性之一:state

看一段代码和打印的结果

class Weather extends React.Component {
  render() {
    console.log("this:", this);
    return <h1>我是用类定义的组件(适用于复杂组件的定义)</h1>;
  }
}
ReactDOM.render(<Weather />, document.getElementById("test"));

在这里插入图片描述
然后实现一个需求来深入了解state:通过点击页面,炎热/凉爽切换。
我们发现,初始化后的state,值为null(为什么是null,可以看一下初始化数据时的注释)。
在这里插入图片描述
初始化数据

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>react</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入核心库 -->
    <script src="../js/react.development.js"></script>
    <!-- 扩展库 -->
    <script src="../js/react-dom.development.js"></script>
    <!-- 转换jsx转为js -->
    <script src="../js/babel.min.js"></script>
    <script type="text/babel">
      // 1.创建组件
      class Weather extends React.Component {
        /**
         * 构造器中能收到什么数据,取决于new的时候,传的是什么数据
         * new Weather并不是我们操作的,而是react操作的
         */
        constructor(props) {
          // 还没学到props,但得用着,模仿官网写
          // 类本身语法
          super(props);
          // 构造函数中this指向构造函数实例对象
          // 16.8之前,state是{},16.8及之后,是null
          this.state = {
            isHot: true,
          };
        }
        render() {
          console.log("this:", this);
          return <h1>今天天气很炎热</h1>;
        }
      }
      //  2.渲染组件到页面
      ReactDOM.render(<Weather />, document.getElementById("test"));
    </script>
  </body>
</html>

在这里插入图片描述
通过tools手动切换
在这里插入图片描述
接下来写点击事件,注意,先做一个错误示范

    <script type="text/babel">
      // 1.创建组件
      class Weather extends React.Component {
        /**
         * 构造器中能收到什么数据,取决于new的时候,传的是什么数据
         * new Weather并不是我们操作的,而是react操作的
         */
        constructor(props) {
          // 还没学到props,但得用着,模仿官网写
          // 类本身语法
          super(props);
          // 构造函数中this指向构造函数实例对象
          // 16.8之前,state是{},16.8及之后,是null
          this.state = {
            isHot: true,
          };
        }

        // state在Weather的实例对象身上
        render() {
          console.log("this:", this);
          return (
            <h1 onClick={demo()}>
              今天天气很{this.state.isHot ? "炎热" : "凉爽"}
            </h1>
          );
        }
      }
      function demo() {
        console.log("demo被调用");
      }
      //  2.渲染组件到页面
      ReactDOM.render(<Weather />, document.getElementById("test"));
    </script>

我在调用点击事件时,写的是 onClick={demo()}
在控制台会发现,函数被立即执行了
在这里插入图片描述
这是因为
       react在new Weather时,通过实例调用了render方法,想拿到return的值,就要执行<h1 onClick={demo()}>今天天气很{this.state.isHot ? “炎热” : “凉爽”}</h1>,这段代码,当执行到onClick赋值语句时,就要将demo()函数调用的返回值交给onClick作为回调,demo()的返回值是undifend(因为没有retrun任何数据),也就是把undifend交给onClick作为回调,**在demo后加(),这是错误的做法,这种做法将导致demo函数调用。**等到点击时,就调用了undifend,react处理了这一过程,如果是undifend,就没有多余动作。

演示其他常见错误写法
        render() {
          console.log("this:", this);
          return (
            <h1 onClick='demo()'>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h1>
          );
        }

在这里插入图片描述
或者:

        render() {
          console.log("this:", this);
          return (
            <h1 onclick='demo'>今天天气很{this.state.isHot ? "炎热" : "凉爽"}</h1>
          );
        }

在这里插入图片描述

正确写法
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>react</title>
  </head>
  <body>
    <div id="test"></div>
    <!-- 引入核心库 -->
    <script src="../js/react.development.js"></script>
    <!-- 扩展库 -->
    <script src="../js/react-dom.development.js"></script>
    <!-- 转换jsx转为js -->
    <script src="../js/babel.min.js"></script>
    <script type="text/babel">
      // 1.创建组件
      class Weather extends React.Component {
        /**
         * 构造器中能收到什么数据,取决于new的时候,传的是什么数据
         * new Weather并不是我们操作的,而是react操作的
         */
        constructor(props) {
          // 还没学到props,但得用着,模仿官网写
          // 类本身语法
          super(props);
          // 构造函数中this指向构造函数实例对象
          // 16.8之前,state是{},16.8及之后,是null
          this.state = {
            isHot: true,
          };
        }

        // state在Weather的实例对象身上
        render() {
          console.log("this:", this);
          return (
            <h1 onClick={demo}>
              今天天气很{this.state.isHot ? "炎热" : "凉爽"}
            </h1>
          );
        }
      }
      function demo() {
        console.log("demo被调用");
      }
      //  2.渲染组件到页面
      ReactDOM.render(<Weather />, document.getElementById("test"));
    </script>
  </body>
</html>

在这里插入图片描述

实现数据的修改

上文已经将数据渲染到页面中,现在想要修改页面的数据。想要修改数据,首先要拿到state中的isHot,先看一段错误写法

      function demo() {
        console.log("demo被调用");
        // 错误示范
        const { isHot } = this.state;
        console.log("isHot", isHot);
      }

在这里插入图片描述
提示xxx of undefined(reading ‘state’),也就是state of undefined,当xxx为undefined时,undefined.state 就会报这个错误。这里的xxx指的就是this。我们来验证一下:
打印一下this

      function demo() {
        console.log("this", this);
      }

在这里插入图片描述
为什么react在调用demo方法时,this是undefined而不是window?
先来复习一下this的指向问题:

  1. 全局作用域或者普通函数中 this 指向全局对象 window
  2. 方法调用中谁调用 this 指向谁
  3. 在构造函数或者构造函数原型对象中 this 指向构造函数的实例
  4. 箭头函数中指向外层作用域的 this

当前的场景是1,我们再来看一下代码结构和注释:
在这里插入图片描述
在这里插入图片描述
解释一下以上两张图:
图1:
js在执行<h1 onClick={demo()}></h1>时,调用了自定义函数demo,由于babel在编译时,默认开启了严格模式,禁止自定义函数指向window,所以是undefined。
图2:
react中的render方法指向的是Weather实例,所以在render中能够正确打印this,react在执行<ReactDOM.render(, document.getElementById(“test”));< 这段代码时,做了new Weather的操作,虽然能够正确调用demo方法,但由于state在Weather实例对象上,demo中无法拿到state。也就是将自定义函数放到类的外边,页面虽然能够正确渲染,但并不能拿到/修改state中的数据。
接下来将demo方法放在Weather类中:

      class Weather extends React.Component {
        /**
         * 构造器中能收到什么数据,取决于new的时候,传的是什么数据
         * new Weather并不是我们操作的,而是react操作的
         */
        constructor(props) {
          // 还没学到props,但得用着,模仿官网写
          // 类本身语法
          super(props);
          /**
           * 构造函数中this指向构造函数实例对象
           * 16.8之前,state是{},16.8及之后,是null
           * state在Weather的实例对象身上
           */
          this.state = {
            isHot: true,
          };
        }
        // 切换天气
        demo() {
          console.log("this", this);
          const { isHot } = this.state;
          console.log("isHot", isHot);
        }
        // 渲染
        render() {
          console.log("this:", this);
          return (
            <h1 onClick={demo}>
              今天天气很{this.state.isHot ? "炎热" : "凉爽"}
            </h1>
          );
        }
      }

注意,类不是函数体,不需要写function
刷新打印后,我们会发现,控制台报错。提示我们demo is not defined
在这里插入图片描述
来看一下代码和注释:
在这里插入图片描述
上图的意思是:
demo在Weatcher类的原型对象上,只有实例Weatcher才能调用demo,所以得写成this.demo,搞定demo is not defined 以后,点击h1,控制台报错,state of undefined,说明this is undefined。
在这里插入图片描述
来思考一下,为什么demo中的this,是undefined?(这非常重要,思考的步骤,橘色,红色,黄色)
在这里插入图片描述
为什么这里的demo,不是通过Weather调用的?
通过一个例子,来分析

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script>
      class Person {
        constructor(name, age) {
          this.name = name;
          this.age = age;
        }
        study() {
          // study方法放在哪里?类的原型对象上,供实例使用
          // 通过Person实例调用study方法时,study中的this就是Person实例
          console.log("this:", this);
        }
      }
      const p1 = new Person("tom", 18);
      p1.study();
    </script>
  </body>
</html>

在这里插入图片描述
这是没有问题的,然后我们修改一下,并打印

      const p1 = new Person("tom", 18);
      p1.study();
      const x = p1.study;
      x();

在这里插入图片描述
在这里插入图片描述
分析完,再来回头看原来的问题
在这里插入图片描述
这里只是通过类的实例对象,通过原型链查找,找到了demo方法,然后将demo方法交给了onClick作为回调,当点击h1时,是直接从堆里把函数拉出来直接执行,并不是通过实例调用

        demo() {
          // demo放在哪里?---Weatcher的原型对象上,供实例使用
          // 通过Weatcher实例调用demo时,demo中的this就是Weatcher实例
          // --------------
          /**
           * 为什么this是undefined?
           * 1.由于demo是作为onClick的回调,所以不是通过实例调用的,是直接调用
           * 2.类中的方法默认开启了局部严格模式,所以demo中的this为undefined
           */
          console.log("this", this);
        }
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值