首页 > 其他分享 >React学习之类组件的this指向问题

React学习之类组件的this指向问题

时间:2023-09-21 11:03:00浏览次数:39  
标签:changeWeather 调用 指向 demo isHot React 实例 组件

免责声明

我们幼儿园有适合自己看的注释拉满版文档,目标是我奶来都能看懂(不是)。

1. 前置知识

1.1 es6类的简单回顾

      class Person {
        // 构造器
        constructor(name, age) {
          console.log(this); // this 指向类的实例对象(new的作用)
          this.name = name;
          this.age = age;
        }
        // 一般方法,放在了 Person 的原型对象上,因此实例.say() 沿着原型链可以调用该方法
        say() {
          // 【通过实例】调用 say 时,say 中的 this 指向实例
          console.log(`我是${this.name},今年${this.age}`);
        }
      }

      const p1 = new Person('ouo', 18);
      p1.say(); // 【通过实例】调用 say

      // ======================================

      // 继承
      class Student extends Person {
        constructor(name, age, color) {
          // 构造器内使用 this 前
          super(name, age); // 调用父类的 constructor()
          this.color = color;
        }
        call() {
          console.log(this); // 实例调用 ,this 指向实例
        }
        // 子类可以重写从父类继承的方法
        say() {
          console.log(`我是${this.name},今年${this.age},喜欢${this.color}色`);
        }
      }

      const c = new Student('kk', 19, 'red');
      c.say();
      c.toString();

这里是自己 new 实例,实例调用方法;React是帮我们创建类实例对象,用实例对象调用render

2. 类组件的this指向问题

2.1 问题引入

// <script type="text/babel">
	// babel 会给整个 script 开启严格模式
	// ==== 1. 创建类组件 ====
	  class Weather extends React.Component {
	    // 构造函数的执行上下文的 this 绑定 类的实例对象,是 new 的作用
		// props 是 new Xxx() 传递的,而这个 new 的过程是 React 帮我们做的
		// React.Component 的构造器会有一个 值为 null 的 state
		constructor(props) {
		  // super 必须在使用 this 关键字之前调用,调用了父类的构造器
		  super(props);
		  console.log(this, '构造函数内的 this 指向类的实例对象');
		  // 初始化状态
		  this.state = {
			isHot: false,
			wind: '微风',
		  };
		}
		// render 一般会放在 Weather 的 原型对象上
		// render 会调用 1 + n 次,1是初始化的那次 n是状态更新的次数
		// render内的 this 指向类的实例对象,因为  ReactDOM.render(<Weather />, document.getElementById('test')); React会帮我们 new Weather,并且通过 实例.render调用
		render() {
		  console.log(this, 'render内的 this 指向类的实例对象');
		  const { isHot, wind } = this.state;
		  return (
            // 这里是 demo 函数声明提升 + 点击事件回调吧
			<h2 onClick={demo}>
			  今天天气很{isHot ? '炎热' : '凉爽'}
			</h2>
		  );
		}
	  }
	  // 写在 class 外的函数
	  function demo() {
	  // 在此处修改 isHot的值 this.state.isHot
	  // 首先,这个 demo 不是 DOM 事件绑定的回调!btn.addEventListener('xxx', function (e) {});才是 DOM 事件绑定,回调函数内的 this 才指向绑定的元素
	  // click 触发后,才执行 demo 函数,本质是 【window】 调用的,指向 window;又由于 babel 开启了严格模式,因此最终指向 undefined
		console.log(this,'开启严格模式,this 指向 undefined') // this.state.isHot error
	  }

	 // ==== 2. 渲染组件到页面 ====
	  ReactDOM.render(<Weather />, document.getElementById('test'));
// </script>

总结:

  1. 类的构造器的执行上下文的 this 绑定 类的实例对象,是 new 的作用
  2. render内的 this 指向类的实例对象,是因为 react 帮我们实例类组件对象后,通过 实例.render 调用
  3. btn.addEventListener('xxx', function (e) {});才是 DOM 元素事件绑定,回调函数内的 this 才指向绑定的事件源元素;
  4. onClick={demo}html 事件绑定,回调函数的 this 指向 window,但 babel 开启了严格模式,因此最终指向 undefined

2.2 解决this指向问题

在构造器内的 this 指向类的实例对象,可以配合 Function.prototype.bind(),返回一个调用原始函数并将其 this 关键字设置为给定的值的新函数。

// <script type="text/babel">
	  class Weather extends React.Component {
		constructor(props) {
		  super(props);
		  console.log(this, '构造函数的 this 指向实例对象');
		  this.state = {
			isHot: false,
			wind: '微风',
		  };
		  // =========== 解决方式 ===========
		  // this.demo 是沿着 类的实例对象 的原型链调用 demo 方法
		  // this.demo.bind(this) 返回一个【改变了 this 从 undefined 指向为 类的实例对象】后的 this.demo 函数的拷贝;疑问 bind是深拷贝吗还是创建的新函数,疑似需要去学一下bind,this.demo的 this 并没有改?
		  // this.changeWeather 是在【类的实例上】添加一个方法,赋值
		  // 至此,再用类的实例调用 changeWeather 方法时,就会就近调用新方法
		  this.changeWeather = this.demo.bind(this);
		}
		render() {
		  const { isHot, wind } = this.state;
		  return (
		    // 当用户点击按钮时 React 会调用你传递的事件处理函数,这里不必纠结 this.changeWeather 是一个函数而不是一个表达式。
			<h2 onClick={this.changeWeather}>
			  今天天气很{isHot ? '炎热' : '凉爽'}
			</h2>
		  );
		}
		// =========== 问题所在 ===========
		// 写在类中,会在类的原型对象上,如果通过 Weather 实例调用,则 this 指向实例对象
		// onClick={this.demo} 没有调用demo,而是 click 触发后,才执行实例对象原型链上的这个 demo 函数,本质是 【window】 调用的,指向 window;又由于类中的一般方法默认开启局部严格模式,因此最终指向 undefined
		// 所以 this.state 会报错,必须有 this.changeWeather = this.demo.bind(this) 先构造改变指向的新方法 + onClick={this.changeWeather} 来解决问题
		 demo() {
			console.log(this,'类中的一般方法默认开启严格模式,因此指向 undefined',
			'但onClick={this.changeWeather}调用的是实例对象的 changeWeather 方法');
			// this.changeWeather = this.demo.bind(this) 后,方法体内当成 this.changeWeather 写~
			const { isHot } = this.state;
			this.setState({ isHot: !isHot});
		  }
	  }
	
	  ReactDOM.render(<Weather />, document.getElementById('test'));
// </script>

总结:

  1. this.changeWeather = this.demo.bind(this);是在类的实例上添加一个改变了 this 从 undefined 指向为 类的实例对象后的 this.demo 函数的拷贝
  2. onClick={this.changeWeather}在点击后,才触发执行实例对象原型链上的这个 changeWeather 函数
  3. 区分 <h2 onClick={this.changeWeather}><h2 onClick={this.demo}> 打印的 this 不同,即可
  4. React 会调用你传递的事件处理函数,这里不必纠结 this.changeWeather 是一个函数而不是一个表达式

3. state的简写

为什么需要?当有大量事件回调时,需要在构造器内写大量重复代码解决this指向问题。

// <script type="text/babel">
	  class Weather extends React.Component {
		// React.Component 的构造器会有一个 值为 null 的 state
		// 不需要在 new 的时候传参的属性可以省略构造函数直接写赋值语句,这些属性依然会在实例对象身上
		state = {
		  isHot: false,
		  wind: '微风',
		};
		render() {
		  const { isHot, wind } = this.state;
		  return (
			<h1 onClick={this.changeWeather}>
			  今天天气很{isHot ? '炎热' : '凉爽'}
			</h1>
		  );
		}
		// 函数表达式 + 箭头函数
		// 表达式 是 类似 state = xxx 的写法,这样实例身上就有了 changeWeather 方法
		// 箭头函数的 this 是它声明所在的作用域的 this,即实例对象,因此就不需要在构造器中使用 bind 再改变 this 指向
		// why? 指向实例对象
		changeWeather = () => {
		  const isHot = this.state.isHot;
		  this.setState({ isHot: !isHot });
		};
	  }
	  ReactDOM.render(<Weather />, document.getElementById('test'));
// </script>

总结:

省略构造器 + 表达式 + 箭头函数

标签:changeWeather,调用,指向,demo,isHot,React,实例,组件
From: https://www.cnblogs.com/pupyy/p/17717692.html

相关文章

  • react基础操作
    组件之间进行参数传递首先我们创建一个组件,在我们的主程序中把数据传递过去import{useState}from'react'importSOMEfrom'./g6/ant-d-g6'import'./App.css'functionApp(){const[data,setData]=useState<String>('传递参数')return(......
  • 【转载】Vue Provide / Inject 详细介绍(跨组件通信、响应式变化、版本变化)
    版权声明:本文为CSDN博主「前端不释卷leo」的原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/qq_41809113/article/details/122071958 一、背景通常,当我们需要从父组件向子组件传递数据时,我们使用props。想象一下这样的结......
  • vue3的ref、reactive的使用
    一、介绍ref和reactive是Vue3中用来实现数据响应式的API,一般情况下,ref推荐定义基本数据类型,reactive推荐定义引用数据类型(对象或数组) 二、ref与reactive对比<template><p>{{person.name}}</p><p>{{person.long}}</p><p>{{age}}</p><p>{{info.addr......
  • Vue组件懒加载
    在当今快节奏的数字世界中,网站性能对于吸引用户和取得成功至关重要。然而,对于像首页这样的页面,在不影响功能的前提下优化性能就成了一项挑战。这就是Vue组件懒加载的用武之地。通过将非必要元素的加载推迟到可见时进行,开发人员可以增强用户体验,同时确保登陆页面的快速加载。懒......
  • vue父组件值更新子组件没更新
    因为父组件和子组件的数据单向绑定关系,子组件中的数据并不是从父组件中获取的而是通过props传递的。因此只更新父组件的数据不会自动更新子组件中的数据。需要在子组件中通过watch监听num的变化,将最新的值传递给变量,从而更新展示。点击清空button会把num重新赋值,自动触发watch监听......
  • Vue3引入滑块验证组件-2分钟搞定
    手把手视频地址:https://www.bilibili.com/video/BV1Nu4y1r7Wr/安装npminstall--savevue3-slide-verify登录页面引入template下<template><divclass="login"> <el-cardclass="cover"v-if="loginUser.data.user"> <slide......
  • Python从入门到实战-Scrapy源码2-核心组件
    Scrapy核心组件本篇文章解决:Scrapy有哪些核心组件?以及它们主要负责了哪些工作?这些组件为了完成这些功能,内部又是如何实现的?爬虫类上次讲到Scrapy运行起来后,执行到最后到了Crawler的crawl方法,我们来看这个方法:@defer.inlineCallbacksdefcrawl(self,*args,**kwargs)......
  • bus兄弟组件传值
    bus兄弟组件传值,注意:1、监听时机要比发送时机早2、这两个组件不要按需引入,不然会导致首次收不到传的值例:在组件1的mounted里面发送值:this.$bus.$emit('selected',true)在组件2的created里面监听值:this.$bus.$on("selected",(data)=>{})......
  • React 虚拟滚动 长列表
    定高版本1"useclient";2importReact,{useCallback,useMemo,useState}from"react";34interfaceIProps{5list:any[];6fixedHeight:number;7}89//Fixedheight10constVirtualView=(props:IProps)=>{1......
  • Ant Design Vue 中的tab组件中,获取不到$ref
    问题:在tab的组件中引入了一个组件,在页面渲染时,需要用到子组件bpmnModeler里面的方法,调用this.$ref.bpm的时候报错了,找不到bpm<a-tab-panekey="3"tab="流程图"><divclass="search"><a-card><a-rowstyle="positi......