react属性-state状态
一 对state的理解(20)
需要理解的概念有:
- 人->状态->行为与组件->状态->驱动视图之间的比较理解
- 通过查看this,确认实例对象的属性内容主要包括的部分
- 理解组件包括的三大核心属性:state、props、refs
- 理解响应式数据与普通数据之间的差异
<!-- 准备好一个容器 -->
<div id="test"></div>
<script type="text/babel">
const username = 'Vane'
//定义组件
class Demo extends React.Component {
render() {
// 查看this对象,因为this指向的是实例对象
// 所以可以看到实例对象的属性内容
console.log(this);//组件实例Demo{}
return (
<div>
<h3>你好,{username}</h3>
</div>
)
}
}
//组件内容没有变化,有输出但是没有渲染
setTimeout(() =>{
username = "李四"
},1000)
//渲染组件
ReactDOM.render(<Demo />, document.getElementById('test'))
</script>
设置的username并不是一个响应式的数据内容,我们期望实现的是响应式数据的更新。
当前变量声明方式与响应式的方式从生活化编程理解:
- 初始阶段都是一样的
- 后续阶段将出现问题,没办法实现响应式数据的修改
案例:
- 张三买了一款手机,花费3000,但没买手机屏幕保险。手机屏幕摔碎,后期不管。
- 李四买了一款手机,花费3800,但买了手机屏幕保险。手机屏幕摔碎,不管几次,后期只要屏幕碎了,就直接换新。
二 初始化state
需要理解的概念有:
- this中的context、props、state、refs等内容由谁协助完成
- undefined、null、空对象的差异与区别
- 为什么state最终需要设置成对象模式,而不是数组等数据类型(数组为有序、对象则无序)
- 在哪里设置state,为什么在constructor中进行state的设置
- constructor中属性与状态的代码顺序是如何
- JSX中如何编写注释
- state能直接渲染的数据类型有哪些
<div id="test"></div>
<script type="text/babel">
let username = "张三"
//定义组件
// this中的context、props、state、refs等内容由谁协助完成?
// 是由父类(父组件)React.Component完成,由子类继承
class Demo extends React.Component {
render() {
console.log(this); // this指向组件实例对象
return (
<div>
<h2 >你好,{username}</h2>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Demo />, document.getElementById('test'))
</script>
<div id="test"></div>
<script type="text/babel">
//定义组件
class Weather extends React.Component {
// 构造器方法
// 用于:给实例自身初始化属性,且可以接到new调用时传入的参数
// 我们不光要继承父类的属性,还可能设置自己当前类的新属性
// 所以需要利用 constructor 来进行当前类的属性传递与设置
// state 之前打印的时候是一个实例对象的属性,
// 也可以在构造函数中对它进行初始化设置操作
constructor(props) {
super(props) // 先进行父类属性的继承接收
console.log(this) //实例对象
// 才会有 this 对象的应用,所以super前是没有this对象的
console.log(this.state) //之前是null,有空间,但空间里没有值,undefined
this.state = { //只能设为null或者对象(数组为有序、对象则无序)
isHot: true //可以是任何数据类型
}
}
render() {
console.log(this.state.isHot); //true
return (
<div>
{/* 渲染:
界面中isHot并不显示,是因为数据类型的问题,现在是布尔型,不显示
哪些数据类型可以在JSX中直接渲染?
可以是:string/number/array、三元运算
不能是:对象、函数等数据类型
*/}
<h2 >今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
三 react中绑定事件的写法
需要理解的概念有:
- 原生JavaScript中事件绑定的操作方式有哪几种
- 虚拟DOM与真实DOM转换的顺序以及DOM对象获取与事件监听
- 利用原生模式的事件绑定尝试在React的JSX中进行测试
- React的JSX中函数调用与函数指向差异
1.原生JavaScript中事件绑定的操作方式有哪几种
<body>
<button id="btn1">按钮一</button>
<button id="btn2">按钮二</button>
<!-- demo必须加()号,如果不加,则无法触发 -->
<!-- js 绑定点击事件的方法 -->
<button onclick="demo()">按钮三</button>
<script>
const btn1 = document.getElementById('btn1')
const btn2 = document.getElementById('btn2')
// 事件监听绑定方式
btn1.addEventListener('click', function () {
alert('按钮一被点击了')
})
// onclick绑定方式
btn2.onclick = function () {
alert('按钮二被点击了')
}
function demo() {
alert('按钮三被点击了')
}
</script>
2.虚拟DOM与真实DOM转换的顺序以及DOM对象获取与事件监听
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<script type="text/babel">
//定义组件
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {
isHot: false
}
}r
render() {
return (
<div>
<h2 id="h2">今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
</div>
)
}
}
/*
const h2 = document.getElementById('h2');
h2.addEventListener('click', function () {
console.log(h2) //null
alert('真实DOM渲染之前找不到DOM对象,h2返回的是null,所以无法实现对象监听')
})
*/
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
h2.addEventListener('click', function () {
alert('真实DOM渲染以后可以找到DOM对象进行事件监听')
})
// h2.onclick = function () {
// alert('真实DOM渲染以后可以找到DOM对象进行事件监听')
// }
</script>
3.利用原生模式的事件绑定尝试在React的JSX中进行测试
render() {
console.log(this);
return (
<div>
{/*
将出错,提示是否改用驼峰式命名法:
Invalid event handler property `onclick`.
Did you mean `onClick`
如果采用驼峰式写法仍旧会出现如下错误:
Expected `onClick` listener to be a function,
instead got a value of `string` type
onclick = "demo()",但是其实onclick内容绑定的是字符串
*/}
<h2 id="h2" οnclick="demo()">
今天天气很{this.state.isHot ? '炎热' : '凉爽'}
</h2>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
function demo() {
alert('事件的绑定与调用')
}
4.React的JSX中函数调用与函数指向差异
render() {
console.log(this);
return (
<div>
{/*
1 加了小括号是函数的调用,
2 不加小括号是函数的指向,
3 没有加小括号其实是函数的指向,最终不能加小括号
*/}
<h2 id="h2" onClick={demo()}>
今天天气很{this.state.isHot ? '炎热' : '凉爽'}
</h2>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
function demo() {
alert('事件的绑定与调用')
}
JSX的事件绑定注意事项:
1 react的事件有很多种,它和原生事件非常的相似,比如说onclick,ondbclick,onmousemove,但是它的命名规则是驼峰式写法,比如:onClick,onDoubleClick,onMouseMove
2 在进行事件绑定的时候,需要利用{},不能是字符串
3 {}里面不需要进行()的处理,不然则就是函数调用,而不是函数指向
四 类组件中事件回调this丢失问题
需要理解的概念有:
- 命名函数、匿名函数中的this以及函数定义的顺序与事件触发的关系
- 通过改变块区作用域中的变量获取this对象
- 函数指向与函数调用以及this指向的明确
- 模拟类中的方法被React作为事件回调去使用
1.命名函数、匿名函数中的this以及函数定义的顺序与事件触发的关系
<script type="text/babel">
// ES6箭头函数也没办法找到this对象
// const changeWeather = () => {
// console.log('this:', this) //严格模式,不指向window,this是undefined
// }
//定义组件
class Weather extends React.Component {
constructor(props) {
super(props)
// 先进行父类属性的继承接收
// 才会有 this 对象的应用,所以super前是没有this对象的
this.state = {
isHot: false
}
}
render() {
console.log(this);
return (
<div>
<h2 id="h2" onClick={changeWeather}>
今天天气很{this.state.isHot ? '炎热' : '凉爽'}
</h2>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
// 命名函数,函数提升
// function changeWeather() {
// console.log('this:', this);
// }
// 匿名函数,调用不到,需要将函数定义在代码前面
// const changeWeather = () => {
// console.log('this:', this)
// }
</script>
2.通过改变块区作用域中的变量获取this对象
<div id="test"></div>
<script type="text/babel">
let that; //let指向组件实例对象
const changeWeather = () => {
console.log('this:', that)
}
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {
isHot: false
}
that = this //将this给到that,让that可以指向组件的实例对象
}
render() {
console.log(this);
return (
<div>
<h2 id="h2" onClick={changeWeather}>
今天天气很{this.state.isHot ? '炎热' : '凉爽'}
</h2>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
思考的问题1:如果都把事件监听回调写在组件外部,那么组件封装的意义在哪?
思考的问题2:为什么需要采用组件化开发?组件化开发的组件中应该包含哪些内容?
3.函数指向与函数调用以及this指向的明确
<div id="test"></div>
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {
isHot: false
}
}
/*类的一般方法
changeWeather放在哪里?
Weather的原型对象上,通常给实例对象(组件实例)使用,通过实例可以看见
changeWeather中的this是谁?
通过Weather的实例调用,那么changeWeather中的this就是Weather的实例
*/
changeWeather() {
console.log('this:', this);
}
render() {
console.log(this);
return (
<div>
{/*
直接changeWeather将无法调用函数
需要清楚changeWeather的位置与其中的this
所以需要将changeWeather修改为this.changeWeather
但修改成this.changeWeather以后,
changeWeather函数中的this却为undefined
这是因为this.changeWeather只是指向,并不是函数调用
如果修改成this.changeWeather(),这个this指向的是实例对象
并直接调用函数,则可以打印出this对象
*/}
<h2 id="h2" onClick={this.changeWeather}>
今天天气很{this.state.isHot ? '炎热' : '凉爽'}
</h2>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
4.模拟类中的方法被React作为事件回调去使用
<script>
class Weather {
changeWeather() {
console.log(this);
}
}
const w = new Weather();
// 直接调用,this指向Weather
w.changeWeather()
// 没有调用,只是指向
const x = w.changeWeather;
// 进行函数调用,this输出为undefined
// 因为现在的x()是全局,this如果不是严格模式应该是指向window
// 但当前是class类,所以自动严格模式,所以是undefined
// 严格模拟:如果this指向window才进行管理,
// 不指向window是不作以管理的
x();
</script>
五 构造器中写bind去解决this问题
需要理解的概念有:
- 如何改变this指向,call、apply、bind的差异与区别
- 如何进行组件实例属性的设置
<div id="test"></div>
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {
isHot: false
}
console.log(this) //weater实例,有changeweater
/*
改变this指向的几种方式:
call与apply实现两个功能:改变this指向,并且调用函数
1.call (,,,)
2.apply [,,,]
bind实现两个功能:改变this指向,返回新函数
3.bind,谁绑定改变改的this指向
*/
this.changeWeather = this.changeWeather.bind(this);//this指向组件实例
/*
this.changeWeather: 顺着组件实例,找到原型上的changeWeather方法
this.changeWeather.bind:调用bind生成一个新的函数
this.changeWeather.bind(this):并且新的函数的this已经更改成了组件实例
返回的一个新函数赋值给this.changeWeather,也就是说放到组件实例自身上
只不过放到组件实例自身上的函数名称刚好也叫changeWeather 也可以叫xxx
this.changeWeather = this.changeWeather.bind(this);
*/
// 如果编写this.xxx,则会将相应的xxx设置于组件实例当中
// 所以在组件实例中也可以看到 changeWeather 函数
// this.instance_prop = 'instance_prop'
}
changeWeather() {
console.log('this:', this);
}
render() {
console.log(this);
return (
<div>
{/*第二种方法在进行绑定事件的时候就直接通过bind改变this指向*/}
<h2 id="h2" onClick={this.changeWeather}>
今天天气很{this.state.isHot ? '炎热' : '凉爽'}
</h2>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
六 setState的使用
需要理解的概念有:
- React中不能直接修改state,需要利用setState修改state数据
- 构造器、render函数、事件调用触发的次数,setState更新的模式,组件的复用
1.React中不能直接修改state,需要利用setState修改state数据
<div id="test"></div>
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {
isHot: false
}
this.changeWeather = this.changeWeather.bind(this);
}
changeWeather() {
// console.log('changeWeather');
// React中不允许直接进行state的修改
// this.state.isHot = true;
// 可以通过隐式原型链查看到React.Component所提供的setState方法
// toggle切换
this.setState({ isHot: !this.state.isHot })
}
render() {
console.log(this);
return (
<div>
<h2 id="h2" onClick={this.changeWeather}>
今天天气很{this.state.isHot ? '炎热' : '凉爽'}
</h2>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
2.构造器、render函数、事件调用触发的次数,setState更新的模式,组件的复用
<div id="test"></div>
<hr />
<div id="test2"></div>
<script type="text/babel">
class Weather extends React.Component {
constructor(props) {
console.log('---constructor---') //构造器调用几次? —————— 1次
super(props)
this.state = {
isHot: false,
wind: '微风'
}
this.changeWeather = this.changeWeather.bind(this);
}
changeWeather() {
console.log('changeWeather') // changeWeather调用几次? —————— 点几次调几次
this.setState({ isHot: !this.state.isHot })
}
render() {
console.log('---render---') //render 1+n次,1是初始化那次,n是state改变的次数。
return (
<div>
<h2 id="h2" onClick={this.changeWeather}>
今天天气很{this.state.isHot ? '炎热' : '凉爽'}
</h2>
<h3>风力:{this.state.wind}</h3>
</div>
)
}
}
//渲染组件
ReactDOM.render(<Weather />, document.getElementById('test'))
ReactDOM.render(<Weather />, document.getElementById('test2'))
</script>
七 state的简写方式
需要理解的概念有:
- 类中的赋值语句
- state的简写方式
1.类中的赋值语句
<script type="text/javascript" >
class Student {
constructor(name,age){
this.name = name
this.age = age
}
// console.log('hello');
// 类中无法调用console.log()
// alert('hello')
// 类中无法直接调用函数
// 该行代码的含义是:
// 给实例自身追加一个属性,名为school,值为'university'
// 类中可以直接设置实例属性
school = 'university'
}
const s1 = new Student('老刘',5)
const s2 = new Student('老王',4)
console.log(s1)
console.log(s2)
</script>
2.state的简写方式
<!-- 准备好一个容器 -->
<div id="test"></div>
<script type="text/babel">
/*
1.在开发中,初始化state一般直接使用: state = {}
2 直接写成state={}以后,构造器中方法的bind(this)就不再有
3 那么,函数中的this再次的回到undefined状态,也不会再有this.state
1)可以在方法的指向过程中进行bind(this)处理
2)在开发中,把类中所有事件回调的函数,都写成箭头函数,且直接放在组件实例自身。
*/
class Weather extends React.Component {
//初始化状态
state = { isHot: true }
/*
定义了一个属性,只不过该属性类型为fn函数类型
所以需要将普通函数改造成箭头函数
因为在箭头函数中,this不进行改变,所以仍旧是上层的实例对象
*/
changeWeather = () => {
console.log(this);
const { isHot } = this.state
this.setState({ isHot: !isHot })
}
render() {
console.log(this) //weater{changeweather}
return (
<h2 onClick={this.changeWeather}>
今天天气很{this.state.isHot ? '炎热' : '凉爽'}
</h2>
)
}
/*
// 标准的类的一般方法,将放置于隐式原型当中
changeWeather(){
const { isHot } = this.state
this.setState({ isHot: !isHot })
}
*/
/*
定义了一个属性,只不过该属性类型为fn函数类型
现在的函数类型为普通函数,this为undefined
*/
// changeWeather = function () {
// console.log(this);
// const { isHot } = this.state
// this.setState({ isHot: !isHot })
// }
}
ReactDOM.render(<Weather />, document.getElementById('test'))
</script>
八 总结state
需要理解的概念有:
- state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合,无序对象)
- 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
- 强烈注意
- 组件中render方法中的this为组件实例对象
- 组件自定义的方法中this为undefined,如何解决?
- 强制绑定this:通过函数对象的bind(),可以在构造器中以及在事件指向过程中进行bind()
- 箭头函数,将类的方法改为类的属性模式,通过箭头函数不改变this指向来解决
- 状态数据,不能直接修改或更新,需要利用setState进行状态改新
现在的函数类型为普通函数,this为undefined
*/
// changeWeather = function () {
// console.log(this);
// const { isHot } = this.state
// this.setState({ isHot: !isHot })
// }
}
ReactDOM.render(, document.getElementById(‘test’))
## 八 总结state
需要理解的概念有:
- state是组件对象最重要的属性,值是对象(可以包含多个key-value的组合,无序对象)
- 组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)
- 强烈注意
- 组件中render方法中的this为组件实例对象
- 组件自定义的方法中this为undefined,如何解决?
- 强制绑定this:通过函数对象的bind(),可以在构造器中以及在事件指向过程中进行bind()
- 箭头函数,将类的方法改为类的属性模式,通过箭头函数不改变this指向来解决
- 状态数据,不能直接修改或更新,需要利用**setState**进行状态改新