react三大属性: state(内部状态)、props(外部属性)、refs(表示组件内某个元素)
state:
-
state是组件对象最重要的属性, 值是对象(可以包含多个key-value的组合)
-
组件被称为"状态机", 通过更新组件的state来更新对应的页面显示(重新渲染组件)
一、注意:
-
组件中render方法中的this为组件实例对象
-
组件自定义的方法中this为undefined,如何解决?
a) 强制绑定this: 通过函数对象的bind()
b) 箭头函数
-
状态数据,不能直接修改或更新
二、setState的两种形式:
1、函数形式的setState
setState(updater, [callback])------函数式的setState
1.updater为返回stateChange对象的函数。
2.updater可以接收到state和props。
3.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
test = () => {
// 函数形式的setState,函数形式的setState能够接收到两个参数,一个是state,另一个是props
this.setState(state => ({count: state.count + 1}))
}
2、对象形式的setState
setState(stateChange, [callback])------对象式的setState
1.stateChange为状态改变对象(该对象可以体现出状态的更改)
2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
test = () => {
// 对象形式的setState
const count = this.state.count + 1;
this.setState({count})
}
总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ===> 使用对象方式
(2).如果新状态依赖于原状态 ===> 使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据, 要在第二个callback函数中读取
三、使用过setState之后能否立即获取状态更新后的值(否),如何立即获取到?
test = () => {
// 函数形式的setState,函数形式的setState能够接收到两个参数,一个是state,另一个是props
this.setState(state => ({count: state.count + 1}))
console.log('函数形式的setState更新后:', this.state.count)
}
如何立即获得?
test = () => {
this.setState(state => ({count: state.count + 1}), () => {
console.log('函数形式的setState更新后:', this.state.count)
})
}
四、使用setState()来更新状态时是异步还是同步?(页面同步更新,但是直接console.log打印是未更新的)
判断setState()更新状态时异步还是同步的,主要是看执行setState的位置
-
在React控制的回调函数中(生命周期钩子,react事件监听回调)这种情况是异步的。
-
在非react控制的异步回调函数中(定时器回调/原生事件监听回调/promise回调)这种情况是同步的。
五、异步举例
-
在React事件回调函数中使用setState(异步的)
// React事件回调函数中
update1 = () => {
console.log('React事件回调函数更新之前:',this.state.count);
this.setState(state => ({count: state.count + 1}))
console.log('React事件回调函数更新之后:',this.state.count);
}
-
在生命周期钩子函数中使用setState(异步的)
// 在生命周期钩子函数中
componentDidMount() {
console.log('生命周期钩子函数更新之前:',this.state.count);
this.setState(state => ({count: state.count + 1}))
console.log('生命周期钩子函数更新之后:',this.state.count);
}
六、同步举例
-
setTimeout
// 定时器回调
update2 = () => {
setTimeout(() => {
console.log('setTimeout 更新之前:', this.state.count);
this.setState(state => ({ count: state.count + 1 }))
console.log('setTimeout 更新之后:', this.state.count);
})
}
-
原生onclick
update3 = () => {
const h1 = this.refs.count;
h1.onclick = () => {
console.log('onclick 更新之前:', this.state.count);
this.setState(state => ({ count: state.count + 1 }))
console.log('onclick 更新之后:', this.state.count);
}
}
-
Promise
update4 = () => {
Promise.resolve().then(value => {
console.log('Promise 更新之前:', this.state.count);
this.setState(state => ({ count: state.count + 1 }))
console.log('Promise 更新之后:', this.state.count);
})
}
七、经典面试题
一道关于setState的经典面试题 - CodeSandbox
答案解析(按输出顺序进行解析):
第1个输出: react首先会渲染下组件,此时获取到的count值是state中存的初始值,所以是0.
第2、3输出:执行完render之后,会进入componentDidMount钩子函数,遇到两个对象式的setState会进行合并,但由于此时在钩子函数中,获取state是异步的,所以打印的都是0,但是当遇到函数式的setState,则不会合并,此时count的值已经变为了3.
第4输出:此时componentDidMount中出了Promise和setTimeout外都执行了,上面的代码对JS来说都属于同步代码,此时可以进行更新render了,所以打印了render 3.
第5输出:setTimeout和Promise中,由于Promise是微任务,所以优先执行,在执行的时候,这里的setState是同步更新state的,所以调用一次setState就要调用一次render,所以第五行打印的是render: 4.
第6输出:执行log操作,打印的是Promise: 4.
props:
-
每个组件对象都会有props(properties的简写)属性
-
组件标签的所有属性都保存在props中
作用:
-
通过标签属性从组件外向组件内传递变化的数据
-
注意: 组件内部不要修改props数据
编码操作:
// 1、内部读取某个属性值
this.props.name
// 2、对props中的属性值进行类型限制和必要性限制
Person.propTypes = {
name: PropTypes.string.isRequired,
age:PropTypes.number.
}
// 3、将对象的所有属性通过props传递
<Person {...person}/>
// 4、默认属性值
Person.defaultProps = {
age: 18,
sex: 'male'
}
Refs:
组件内的标签可以定义ref属性来标识自己
编码操作:
// 1、字符串形式的ref
<input ref="input1" />
// 通过this.refs.input1 获取值
// const {input1} = this.refs
// console.log(inout1.value)
// 2、回调形式的ref
<input ref={(c) => {this.inputValue = c}} />
// 通过this.inputValue 获取值
//const {inputValue} = this
//console.log(inputValue.value)
// 3、createRef创建ref容器
myRef = React.createRef()
<input ref={this.myRef} />
// 通过this.myRef.current.value 获取值