在上两节中,我们讲述了props, 组件使用props进行渲染,但是这是一次性的, props渲染完成之后就不做任何事情了,但是现实中却不是这样的,当我们点击购物车上的加减按钮时,数量会自动加1或减1,还有在就是倒计时效果,这就用了组件内部的状态, 如果一个组件有内部状态,那只能用类式组件,因为类中拥有构造函数,构造函数表示就是一个对象实例上的属性,对象实例上的属性都是特有的,所以也可以称之为对象实例的状态。

  在组件的构造函数中声明组件的状态也是很简单,直接写this.state = {},我们在这个对象中添加组件中使用到的状态。如我们写一个加减组件,它需要一个状态保存数字,我们可以写 this.state= {count: 0},然后我们在render函数中就可以this.state.count获取组件中的状态进行渲染。现在我们写一个counter 组件。

class Counter extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

  render() {
    const buttonStyle = {
        margin: '10px'
    };
    return (
      <div>
        <button style={buttonStyle}>+</button>
        <button style={buttonStyle}>-</button>
        <span>count: {this.state.count}</span>
      </div>
    );
  }
}

  你可以注意到,调用构造函数时, 多了super(props) 语句,这是es6 的语法规定, 子类中没有this, 只能调用super生成子类的 this,如果在调用super 之前使用this, 就会报错。 这个地方其实是不会变化的,可以看成一个模式。每次给组件添加状态的时候,我们就按照这个模式书写就可以了。先写

constructor(props) {
    super(props);
}

  要添加什么状态,直接在构造函数里面super下面写this.state = …. 就可以了。

  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

  页面效果如下,组件中获取到了state中定义的状态。

  constructor 构造函数中的props也是指父组件传递过来的props, 这里接受父组件传递过来的props的主要作用是可以使用props对状态进行初始化。比如, this.state = {count : props.initCount} , 这时count就初始化为父组件传递过来的值,而不是0.

  其实constructor 还有一个默认参数 context, 上下文,React的context API, 这个以后再说。

  constructor(props, context) {
    super(props, context)
  this.state = {}
  }

  如果我们的构造函数中,这两个参数都用不到,我们完全可以写空参数的构造函数

constructor() {
  super();
  this.state = {
    count: 0
  }
}

  构造函数说完了,我们也给组件添加了状态,那怎么才能更改状态呢,当点击加号的时候,count 加1, 点击减号, count 减1? 首先要给加减button 添加click事件,然后再在事件中进行加减1的操作,状态的更改

  React 中给元素添加事件,就像我们给DOM元素添加行内事件一样简单,直接在元素身上写事件属性就可以了。但这里也有两点不同,事件名要用驼峰命名法,如click事件要写成onClick,事件处理函数是一个函数,用{} 括起来, <button onClick={handleClick}>点击</button>。那么这又引出了另外一个问题,handleClick 函数写在什么地方? 这里用到了ES6中类的语法,我们在一个类中写函数,该函数会自动绑定到类的原型上,对象实例通过 this 就能获取到。相对应地,在组件类中,我们直接写事件处理函数,然后在render函数中通过this 获取。现在我们给button 添加click 事件

class Counter extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

    // 加号事件处理函数
    handleClickIncrement() {
      console.log('加1')
   }

  // 减号事件处理函数
  handleClickDecrement() {
    console.log('减1')
  }
  render() {
        const buttonStyle = {
            margin: '10px'
        };
    return (
      <div>
        {/*button 添加事件处理函数*/}
        <button style={buttonStyle} onClick={this.handleClickIncrement}>+</button>
        <button style={buttonStyle} onClick={this.handleClickDecrement}>-</button>
        <span>count: {this.state.count}</span>
      </div>
    );
  }
}

  现在点击加减按钮,控制台上可以看到加1或减1出现,表明我们绑定事件成功。现在就要改变状态了,对于状态的改变,React不允许直接更改状态, 或者说,我们不能给状态(如: count)进行赋值操作,  为此react 提供了一个 setState函数,必须调用它来更改状态。它接受一个对象或一个函数作为参数,当接受一个对象时,对象的键就是要改变的状态,对象的值就是状态改变之后的值。当接受一个函数时,react 自动注入两个参数,一个是prevState, 一个props,然后返回一个对象,返回的对象和它接受的对象是一致的,键是要改变的状态,值就是状态更改后的值。对于prevState, 它指的就是组件的上一次状态,构造函数中this.state对象。Props,很好理解,就是这个组件接受的属性。我们在handleClickIncrement 和handleClickDecrement 事件处理函数中调用this.setState方法来更改状态。

    // 加号事件处理函数
    handleClickIncrement() {
    this.setState({
            count: this.state.count + 1
        })
  }

    // 减号事件处理函数
  handleClickDecrement() {
    this.setState({
            count: this.state.count - 1
        })
  }

  这时点击加或减,控制台竟然报错了,

  我们只使用了this.setState, 难道 this 是undefined, 我们在handleClickIncrement 和handleClickDecrement 事件处理函数中console.log(this), 发现它确定是undefined

// 加号事件处理函数
handleClickIncrement() {
    console.log(this);
    this.setState({
            count: this.state.count + 1
        })
  }

  也就是this 并没有指向React 组件实例,看来我们要修正this的指向问题,使它指向我们这个组件实例。改变this指向有以下几种方法:

  一种是ES5提供的bind()方法,它的第一个参数就是指定函数中this的,且它返回 一个函数,可以知道,返回的这个函数中this已经写死了,在程序运行的时候也不会变化了。我们在什么位置上使用bind方法? 在构造函数中,因为构造函数中的this, 就是具体创建的对象实例,在构造函数中把this 传递给bind作为第一个参数,bind 返回的参数中的this 就固定成了对象实例。构造函数中修改如下,现在点击加减没有问题了。

constructor(props) {
    super(props);
    this.state = {
        count: 0
    }

    // bind方法绑定this
    this.handleClickIncrement = this.handleClickIncrement.bind(this);
    this.handleClickDecrement = this.handleClickDecrement.bind(this);
}

  另一种是箭头函数,因为箭头函数里面的this继承于包围它的函数,那么我们直接把click 事件处理函数写成箭头函数,里面的this 就指向我们这个组件。

// 事件处理函数赋值一个箭头函数
handleClickIncrement = () => {
    this.setState({
        count: this.state.count + 1
    })
}

handleClickDecrement = () => {
    this.setState({
        count: this.state.count - 1
    })
}

  注意这种写法还没有被浏览器支持,React 库是默认支持的,所以在这里是没有问题的。如果你以后使用wepback等构建工具时,要自己进行配置。

  其实箭头函数还有一种写法,就是我们在绑定事件处理函数的时候,不直接写this. 而是写一个箭头函数,

 <button style={buttonStyle} onClick={() => this.handleClickIncrement()}>+</button>
 <button style={buttonStyle} onClick={() => this.handleClickDecrement()}>-</button>

  这种方法不太常见,因为写起来比较麻烦,但是它有一个好处,因它是一个函数,我们可能把实例上的属性传递给事件处理函数。

  刚才提到过setState还可以接受一个函数,React 为它注入prevState 参数和props 参数,我们这里,只用到prevState 参数。把加号事件处理函数用函数写一下。

    // 加号事件处理函数
    handleClickIncrement() {
    this.setState(prevState => {
            console.log(prevState);
            return {
                count: prevState.count + 1
            }
        })
  }

  我顺便打印了一下prevState, 可以看到,页面上显示6,而prevState是5, 它就是更改之前的状态,我们只有获取到更改之前的状态,才能修改它,进而形成新的状态。

  这里对事件处理函数做一下解释,虽然它的语法看着像行内事件,但它是用了事件委托的方式来处理事件。无论有多少个事件出现,其实最后都只在DOM树上添加一个事件处理函数,挂在最顶层的DOM节点上。所有的事件都被这个事件处理函数捕获,然后根据具体组件分配到特定函数,使用事件委托的性能当然比为每一个元素添加事件性能更好。因为React 控制了组件的生命周期,在unmount的时候,自然能够清除相关的所有事件处理函数,从而不会造成内存泄露。

React 学习(三) ---- state 和 事件处理函数的更多相关文章

  1. React学习之State

    本文基于React v16.4.1 初学react,有理解不对的地方,欢迎批评指正^_^ 一.定义组件的两种方式 1.函数定义组件 function Welcome(props) { return & ...

  2. react学习(三)之生命周期/refs/受控组件 篇

    挂载/卸载 //在类组件中 class Clock extends React.Component { constructor(props) { super(props); this.state = ...

  3. react中 props,state与render函数的关系

    我们很明显的能够感受到,react是一门数据驱动的框架,当数据发生变化,页面就会自动发生变化,他背后的原理是怎么样子的呢 比如todolist例子里面,inputValue变了,框里面的内容就会自动变 ...

  4. react学习三

    三点运算符  (...)的用法 1:展开运算符 let a=[1,2,3]; let b=[0,...a,4];//[0,1,2,3,4] let obj ={a:1,b:2}; let obj2 = ...

  5. python学习 (三十) python的函数

     1: 函数参数默认值 def method1(p1 = , p2 = ): // 函数有两个参数,并且都有默认值 return p1 + p2 print(method1()) print(meth ...

  6. python学习三十五天函数递归的用法

    python函数递归就是自己调用自己,无限循环,但是python限制了调用的次数1000次,就会终止,递归用在栏目分类,采集程序比较多,下面简单说函数递归用法和实例 1,函数递归用法 def func ...

  7. python学习三十四天函数高阶函数定义及用法

    python函数高阶函数是把函数当成一个变量,传递给函数作为参数,或者函数的返回值里面有函数,都称为高阶函数, 1,把函数作为参数传递 def dac(x,y): return x+y def tes ...

  8. 为什么React事件处理函数必须使用Function.bind()绑定this?

    最近在React官网学习Handling Events这一章时,有一处不是很明白.代码如下: class Toggle extends React.Component { constructor(pr ...

  9. React学习笔记(六)事件处理

    React学习笔记(六) 五.事件处理 React事件绑定属性的命名采用驼峰写法,不同于传统DOM全部小写. 如果采用JSX的语法,事件函数需要用大括号{}包裹函数名,不同于传统DOM字符串小括号的方 ...

随机推荐

  1. # 20175329 2018-2019-2 《Java程序设计》 第二周学习总结

    学号 2018-2019-3<Java程序设计>第二周学习总结 教材学习内容总结      第二三章与我们所学习的C语言有很多的相似点,在这里我想主要就以我所学习的效果来讨论一下JAVA与 ...

  2. Mac支持ntfs格式的移动硬盘读写操作

    转好文:https://blog.csdn.net/u013247765/article/details/77932144 本机环境: macOS Sierra version 10.12.6 201 ...

  3. 长期招收linux驱动工程师

    公司:宝存科技 工作内容: 1.负责企业级ssd的feature设计和开发工作 2.负责ftl算法的设计及开发 3.排查客户问题 任职要求: 1.精通C语言 2.熟练掌握linux操作系统使用 3.熟 ...

  4. IntelliJ IDEA(一) :安装与破解

    前言 我是从eclipse转IDEA的,对于习惯了eclipse快捷键的我来说,转IDEA开始很不习惯,IDEA快捷键多,组合多,记不住,虽然可以设置使用eclipse的快捷键,但是总感觉怪怪的.开始 ...

  5. 【博客迁移】hyrepo.com

    博客迁移至 www.hyrepo.com

  6. 牛客练习赛B题 筱玛的排列(找递推规律)

    链接:https://ac.nowcoder.com/acm/contest/342/B来源:牛客网 筱玛的排列 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 524288K,其他语 ...

  7. 通过设置线程池的最小线程数来提高task的效率,SetMinThreads。

    http://www.cnblogs.com/Charltsing/p/taskpoolthread.html task默认对线程的调度是逐步增加的,连续多次运行并发线程,会提高占用的线程数,而等若干 ...

  8. echarts x轴 增加滚动条

    charts x轴 增加滚动条 在option 配置项中添加 [ dataZoom 中配置 ] 设置x轴滚动条 效果图: 动态拖动 以下参考代码 dataZoom配置 官网写法 option = { ...

  9. Python_线程、线程效率测试、数据隔离测试、主线程和子线程

    0.进程中的概念 三状态:就绪.运行.阻塞 就绪(Ready):当进程已分配到除CPU以外的所有必要资源,只要获得处理机便可立即执行,这时的进程状态成为就绪状态. 执行/运行(Running)状态:当 ...

  10. 07-nodejs中npm的使用

    NPM是什么? 简单的说,npm就是JavaScript的包管理工具.类似Java语法中的maven,gradle,python中的pip. 安装 傻瓜式的安装. 第一步:打开https://node ...