小胖墩er 小胖墩er
首页
  • 前端文章

    • JavaScript
    • Vue
    • ES6
    • Git
  • Vue
  • React
  • HTML
  • CSS
  • 工具类
  • GitHub技巧
  • 博客搭建
  • 友情链接
💖关于
💻收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

小胖墩er

Better later than never.
首页
  • 前端文章

    • JavaScript
    • Vue
    • ES6
    • Git
  • Vue
  • React
  • HTML
  • CSS
  • 工具类
  • GitHub技巧
  • 博客搭建
  • 友情链接
💖关于
💻收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基本知识

    • React基础
    • React组件三大核心属性 - State
    • React组件三大核心属性 - Props
    • React组件三大核心属性 - Refs
  • Hooks

  • React17新版本概念

  • React笔记
  • 基本知识
小胖墩er
2021-10-22

React组件三大核心属性 - State

# 一、state的理解

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

向组件中添加局部状态:

组件的局部状态(16.8版本之前)只存在于class声明的组件,函数声明的组件无state 所以我们称函数声明的组建为无状态组件,16.8版本后react 添加了Hook的概念让函数组件拥有了局部状态(无状态组件说法就被舍弃了)

提示

除了使用外部数据(通过 this.props 访问)以外,组件还可以维护其内部的状态数据(通过 this.state 访问)。当组件的状态数据改变时,组件会再次调用 render() 方法重新渲染对应的标记。

// 1、创建类组件
class ComponentA extends React.Component {
    constructor(props) {
        super(props);
        // 初始化状态
        this.state = {
            name: '小明', 
            age: 18
        }
    }
    render() {
        //  读取状态
        const { name, age } = this.state;
        return (
            <div>
                <h2>我是组件A</h2>
                <p>render函数就是我的渲染函数</p>
                <p>name: {name}</p>
                <p>age: {age}</p>
            </div>
        )
    }
}
// 2、渲染组件到页面
ReactDOM.render(<ComponentA />, document.getElementById('test'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

注意:

  1. 每个class都由constructor方法,当class代码中没有显示声明constructor构造函数时,class会自动隐式添加该方法
  2. 使用class继承的类,在其constructor构造函数中必须首先调用构造函数自带的super方法在继承的父类中完成this对象的塑造并继承父类的属性方法.如果不调用该方法子类将没有this对象
  3. 如果你的react class组件内部不需要创建state 绑定方法或者任何在constructor构造函数中要执行的代码推荐隐式创建constructor

# 二、react中的事件绑定

onClick 而不是 onclick,react中把方法是小写的都重写了一遍,比如onblur ==> onBlur

注意

onClick={}, 不能写成onClick={test()}因为页面进来执行render()之后会立即执行test方法,把undefined赋给了onClick,所以当你再次点击标题的时候是没有反应的,因为此时为undefined,react中如果遇到是undefined是不做任何动作的

class ComponentA  extends React.Component {
    constructor(props) {
        super(props);
        // 初始化状态
        this.state = {
            isHot: true
        }
    }
    render() {
        //  读取状态
        const { name, age } = this.state;
        return (
            <div>
                <h2>我是组件A</h2>
                <p>render函数就是我的渲染函数</p>
                <p>name: {name}</p>
                <p>age: {age}</p>
                <p onClick={test}>点我</p>
            </div>
        )
    }
}

function test() {
    console.log('我被点击了');
}

ReactDOM.render(<ComponentA  />, document.getElementById('test'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 三、类中方法中的this

class Weather extends React.Component {
    constructor(props) {
        super(props);
        // 初始化状态
        this.state = {
            isHot: true
        }
    }
    render() {
        //  读取状态
        const { isHot } = this.state;
        // changeWeather 作为 onClick 的回调,是直接原型链中找到存放起来,然后再赋值给onClick,,不是通过实例调用的,而是直接调用,所以 this 不指向实例
        // return <h1 onClick={changeWeather}>今天天气很{isHot ? '炎热' : '凉快'}</h1>  error: changeWeather is not defined
        return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉快'}</h1>
    }
    changeWeather() {
        console.log(this); // undefined  因为changeWeather不是实例对象调用的
    }
}

ReactDOM.render(<Weather />, document.getElementById('test'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

问题一:定义的changeWeather方法是位于什么位置的?

答: Weather的原型对象上,供实例使用,通过Weather实例调用changeWeather时,changeWeather中的this就指向Weather实例

问题二:其中的this是什么?

答: undefined,因为类中定义的方法在局部自动开启了严格模式,所以this为undefined

# 四、this指向问题

我们上面已经说了调用了方法之后this是undefined,那这个问题怎么解决呢?

这里使用bind() 这个方法,只需在constructor中改变方法的this指向问题。

constructor(props) {
    super(props);
    // 初始化状态
    this.state = {
        isHot: true
    }
    // 改变this指向
    this.changeWeather = this.changeWeather.bind(this);
}
1
2
3
4
5
6
7
8
9

问题:bind做了什么?

答: bind: 做了两件事情 ---- 生成新的函数并且改变this为Weather的实例对象;this.changeWeather是原型上的方法,通过bind改变this之后生成新的方法放在了实例自身上,导致了实例中也有changeWeather这个方法,这样就能进行调用了。

注意: 没有执行bind之前this为undefined,因为类中定义的方法在局部自动开启了严格模式,所以this为undefined

image.png

执行bind之后this指向Weather的实例对象

image.png

# 五、组件中局部状态更新问题:

我们打印React class组件的state发现,它并没有像Vue一样给每个state属性进行数据劫持,只是一个普通对象。这样就导致如果直接修改class 组件state是不会引起页面刷新的。

在React中修改局部状态state,必须使用this.setState()。

// Wrong 此代码不会重新渲染组件
this.state.age = 20; 

// Correct 
this.setState({age: 20});
1
2
3
4
5

this.setState是浅合并,也就是说this.setState({age})完整保留了this.state.name,但完全替换了this.state.age。你可以调用 setState() 独立地更新state中任意属性

# 六、setState的使用

这里其实说的就是第五点中的状态更新问题

  1. 状态里的数据不允许直接更改,要借助一个内置的API更改
  2. 状态必须通过setState进行更改,且更新是一种合并操作,不是替换
// Wrong 此代码不会重新渲染组件 
this.state.age = 20; 

// Correct 
this.setState({
    age: 20
});
1
2
3
4
5
6
7

# 七、State的简写

其实这个方式也可以解决上面提到的this为undefined的问题

但是有一点不同的是:没有使用构造器,方法改为箭头函数,然后state我们直接写成一个对象,就像那个普通对象添加属性,有就进行替换,没有就新增。

class Weather extends React.Component {

    // 初始化状态
    state = { isHot: true, wind: '大风' }

    render() {
        const { isHot, wind } = this.state;
        return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}, {wind}</h1>
    }

    // 自定义方法  --- 要用赋值语句的形式+箭头函数
    changeWeather = () => {
        // 箭头函数本身没有this,this是外层的this,即Weather
        const isHot = this.state.isHot;
        this.setState({
            isHot: !isHot
        })
        // console.log(this);   // Weather
    }
}

ReactDOM.render(<Weather />, document.getElementById('test'));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 八、小总结

  1. 组件中render方法中的this为组件实例对象

  2. 组件自定义的方法中this为undefined,怎么解决?

    • 强制绑定this: 通过函数对象的bind() 【构造器写法】

    • 赋值语句+箭头函数 【无构造器写法】

  3. 状态数据,不能直接修改或更新,必须通过setState进行更改

在线编辑 (opens new window)
#React
上次更新: 2021/11/14, 07:48:46
React基础
React组件三大核心属性 - Props

← React基础 React组件三大核心属性 - Props→

最近更新
01
毛玻璃效果
11-23
02
svg基本绘制
11-23
03
滑动登录界面
11-23
更多文章>
🖥️

© 2021 小胖墩er 💌 粤ICP备2021158933号 🛀 Theme by 💝 Vdoing

  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×