React组件实例更改state状态值(四)

简介: 【8月更文挑战第14天】React组件实例更改state状态值(四)

代码演示

如果我们想通过函数来更改类中的一个state状态值,代码可能是这样的

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>hello_react</title>
  </head>
  <body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="./js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="./js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="./js/babel.min.js"></script>
    <script type="text/babel">
      // 1、创建类组件
      class MyComponent extends React.Component {
    
    
        constructor(props) {
    
    
          super(props);
          this.state = {
    
     isHot: true };
          this.tel = this.tel.bind(this);
        }
        render() {
    
    
          return <h1 onClick={
    
    this.tel}>今天天气很{
    
    this.state.isHot ? "热" : "冷"},我想吃冰激凌</h1>;
        }
        tel() {
    
    
          const isHot = this.state.isHot;
          this.setState({
    
     isHot: !isHot });
        }
      }
      // 渲染组件
      ReactDOM.render(<MyComponent />, document.getElementById("test"));
    </script>
  </body>
</html>

我们通过this.tel = this.tel.bind(this);改变函数中this的指向,通过setState来更新state状态值。
要理解这个代码的意义,我们需要深入理解一下类组件中的this指向。

类组件中的this指向

函数中的this指向

<script type="text/babel">
  // 1、创建类组件
  class MyComponent extends React.Component {
   
   
    constructor(props) {
   
   
      super(props);
      this.state = {
   
    isHot: true };
    }
    render() {
   
   
      return <h1 onClick={
   
   this.tel}>今天天气很{
   
   this.state.isHot ? "热" : "冷"},我想吃冰激凌</h1>;
    }
    tel() {
   
   
      console.log("类中的this", this);
    }
  }
  // 渲染组件
  ReactDOM.render(<MyComponent />, document.getElementById("test"));
</script>

我们直接打印下this的值
image.png
可以看到,当函数tel被执行时,其代码中的this指向是undefined。

根据类的知识,我们能梳理出一下信息:

  • tel方法是定义在MyComponent的原型对象上,供实例使用
  • tel方法作为onClick的回调,不是由实例对象直接调用的,而是直接调用的

我们知道, this的指向本质上是指向调用者的,即谁调用函数或方法,this就指向这个调用者!由于tel函数不是实例调用的,因此,必然没有指向MyComponent,this也就是undefined了。

更改函数中this的指向

我们需要通过函数来更改state中的值,必须在函数中可以访问组件实例对象才行。因此,我们需要通过bind改变其this指向
我们先复习一下bind的用法

bind(thisArg, arg1, arg2, arg3, ...)

fn.bind的作用是只修改this指向,但不会立即执行fn;会返回一个修改了this指向后的fn。需要调用才会执行:bind(thisArg, arg1, arg2, arg3, ...)()。bind的传参和call相同。

现在,我们在看这句代码,它的作用很明显

this.tel = this.tel.bind(this);

image.png
通过 this.tel在MyComponent上创建了一个tel属性,这个属性值是一个函数,函数来自MyComponen原型上的方法tel,且函数tel的this指向MyComponent自身
或者,我们这么写更便于理解

class MyComponent extends React.Component {
   
   
  constructor(props) {
   
   
    super(props);
    this.state = {
   
    isHot: true };
    this.telFuc = this.tel.bind(this);
  }
  render() {
   
   
    return <h1 onClick={
   
   this.telFuc}>今天天气很{
   
   this.state.isHot ? "热" : "冷"},我想吃冰激凌</h1>;
  }
  tel() {
   
   
    console.log(this);
    const isHot = this.state.isHot;
    this.setState({
   
    isHot: !isHot });
  }
}

image.png

更改类中的state状态值

根据最初的示例代码,我们知道,state状态值的更改是借助setState函数完成的,我们如果想使用react的双向数据绑定,就不能直接修改state的值,比如下面的我种方式不会让页面在点击时有视图更新

  // 1、创建类组件
  class MyComponent extends React.Component {
   
   
    constructor(props) {
   
   
      super(props);
      this.state = {
   
    isHot: true };
      this.telFuc = this.tel.bind(this);
    }
    render() {
   
   
      return <h1 onClick={
   
   this.telFuc}>今天天气很{
   
   this.state.isHot ? "热" : "冷"},我想吃冰激凌</h1>;
    }
    tel() {
   
   
      this.state.isHot = !this.state.isHot;
    }
  }
  // 渲染组件
  ReactDOM.render(<MyComponent />, document.getElementById("test"));

setState函数来自承自父类React.Component原型上的方法,因此,我们可以直接访问到。
image.png
更新状态值的基础方法如下

class MyComponent extends React.Component {
   
   
  constructor(props) {
   
   
    super(props);
    this.state = {
   
    isHot: true };
    this.telFuc = this.tel.bind(this);
  }
  render() {
   
   
    return <h1 onClick={
   
   this.telFuc}>今天天气很{
   
   this.state.isHot ? "热" : "冷"},我想吃冰激凌</h1>;
  }
  tel() {
   
   
    console.log(this);
    this.setState({
   
    isHot: !this.state.isHot });
  }
}

构造函数中每个函数执行的次数

<script type="text/babel">
  // 1、创建类组件
  class MyComponent extends React.Component {
   
   
    constructor(props) {
   
   
      console.log("constructor执行了");
      super(props);
      this.state = {
   
    isHot: true };
      this.telFuc = this.tel.bind(this)  }
    render() {
   
   
      console.log("render函数执行了");
      return <h1 onClick={
   
   this.telFuc}>今天天气很{
   
   this.state.isHot ? "热" : "冷"},我想吃冰激凌</h1>;
    }
    tel() {
   
   
      console.log("tel函数执行了");
      this.setState({
   
    isHot: !this.state.isHot });
    }
  }
  // 渲染组件
  ReactDOM.render(<MyComponent />, document.getElementById("test"));
</script>

GIF 2023-5-10 14-38-56.gif
通过上图我们可以看出,由于MyComponent组件被实例化一次,因此constructor函数执行一次
render函数和原型上的 tel方法则在每次点击页面时执行一次

state的简写方法

state更改值的普通写法

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>hello_react</title>
  </head>
  <body>
    <!-- 准备好一个“容器” -->
    <div id="test"></div>
    <!-- 引入react核心库 -->
    <script type="text/javascript" src="./js/react.development.js"></script>
    <!-- 引入react-dom,用于支持react操作DOM -->
    <script type="text/javascript" src="./js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script type="text/javascript" src="./js/babel.min.js"></script>
    <script type="text/babel">
      // 1、创建类组件
      class MyComponent extends React.Component {
   
   
        constructor(props) {
   
   
          super(props);
          this.state = {
   
    isHot: true };
          this.telFuc = this.tel.bind(this);
        }
        render() {
   
   
          return <h1 onClick={
   
   this.telFuc}>今天天气很{
   
   this.state.isHot ? "热" : "冷"},我想吃冰激凌</h1>;
        }
        tel() {
   
   
          this.setState({
   
    isHot: !this.state.isHot });
        }
      }
      // 渲染组件
      ReactDOM.render(<MyComponent />, document.getElementById("test"));
    </script>
  </body>
</html>

要更改一个组件中的状态值state,我们需要写一个构造器,在构造器内部初始化state

this.state = {
   
    isHot: true };

同时,需要更改MyComponent原型上函数tel的this指向

this.telFuc = this.tel.bind(this);

state更改值的简便写法

要精简这种写法,我们先复习下类的写法。

class Parent {
   
   
  constructor(name) {
   
   
    this.name = name;
  }
  speak() {
   
   
    console.log("请讲话");
  }
  age = 3;
  eat = function () {
   
   
    console.log("吃饭");
  };
}
const people = new Parent("小明");
console.log("people: ", people);

通过上述代码,我们可以发现,通过constructor我们可以在类自身添加静态属性,通过 age = 3;的形式也可以直接在类上自身添加属性;我们如果直接在类里写函数,则函数是定义在Parent类的原型prototype上的,但如果通过eat = function () {};的形式可以直接在类自身添加静态方法。
image.png
根据这个简写规则,我们可以直接更改类中state更新的代码

<script type="text/babel">
  // 1、创建类组件
  class MyComponent extends React.Component {
   
   
    state = {
   
    isHot: true };
    render() {
   
   
      return <h1 onClick={
   
   this.tel}>今天天气很{
   
   this.state.isHot ? "热" : "冷"},我想吃冰激凌</h1>;
    }
    tel = function () {
   
   
      this.setState({
   
    isHot: !this.state.isHot });
    };
  }
  // 渲染组件
  ReactDOM.render(<MyComponent />, document.getElementById("test"));
</script>

这样看起来代码精简多了!但很可惜,this.setState({ isHot: !this.state.isHot });这句代码不会生效,因为这里的this依旧找不到。
但是,我们将function改成箭头函数的形式就好了

<script type="text/babel">
  // 1、创建类组件
  class MyComponent extends React.Component {
   
   
    state = {
   
    isHot: true };
    render() {
   
   
      return <h1 onClick={
   
   this.tel}>今天天气很{
   
   this.state.isHot ? "热" : "冷"},我想吃冰激凌</h1>;
    }
    tel = () => {
   
   
      console.log(this);
      this.setState({
   
    isHot: !this.state.isHot });
    };
  }
  // 渲染组件
  ReactDOM.render(<MyComponent />, document.getElementById("test"));
</script>

使用箭头函数时,函数内的this指向父级,也就是MyComponent,因此,这个问题就迎刃而解了。
页面效果:GIF 2023-5-10 15-12-55.gif

相关文章
|
7月前
|
缓存 前端开发 JavaScript
React Hooks深度解析与最佳实践:提升函数组件能力的终极指南
🌟蒋星熠Jaxonic,前端探索者。专注React Hooks深度实践,从原理到实战,分享状态管理、性能优化与自定义Hook精髓。助力开发者掌握函数组件的无限可能,共赴技术星辰大海!
React Hooks深度解析与最佳实践:提升函数组件能力的终极指南
|
12月前
|
缓存 前端开发 数据安全/隐私保护
如何使用组合组件和高阶组件实现复杂的 React 应用程序?
如何使用组合组件和高阶组件实现复杂的 React 应用程序?
406 68
|
12月前
|
缓存 前端开发 Java
在 React 中,组合组件和高阶组件在性能方面有何区别?
在 React 中,组合组件和高阶组件在性能方面有何区别?
360 67
|
12月前
|
前端开发 JavaScript 安全
除了高阶组件和render props,还有哪些在 React 中实现代码复用的方法?
除了高阶组件和render props,还有哪些在 React 中实现代码复用的方法?
446 62
|
移动开发 前端开发 API
React 音频播放器组件 Audio Player
本文介绍如何使用React创建音频播放器组件,涵盖核心功能如播放/暂停、进度条、音量控制和时间显示。通过HTML5 `&lt;audio&gt;` 元素和React的声明式状态管理,实现交互式音频播放。常见问题包括控件不响应、进度条无法更新和音量控制失灵,并提供解决方案。此外,还讨论了浏览器兼容性、异步错误处理和性能优化等易错点及避免方法。
958 123
|
前端开发 JavaScript
除了使用Route组件,React Router还有其他方式处理404错误页面吗
除了使用Route组件,React Router还有其他方式处理404错误页面吗
505 58
|
前端开发
React 中高阶组件的原理是什么?
React 中高阶组件的原理是什么?
283 57
|
前端开发 开发者
除了函数组件和类组件,React 还有其他创建组件的方式吗?
除了函数组件和类组件,React 还有其他创建组件的方式吗?
237 57
|
前端开发
如何在React Router中定义404错误页面组件?
如何在React Router中定义404错误页面组件?
444 57
|
前端开发
在 React 中使用高阶组件时,如何避免命名冲突?
在 React 中使用高阶组件时,如何避免命名冲突?
323 56