setState()
setState() 官方文档
参数
setState(updater, [callback])
参数一为带有形式参数的 updater 函数 updater 函数中接收的 state 和 props 都保证为最新。 updater 的返回值会与 state 进行浅合并。
setState(stateChange[, callback])
第一个参数除了接受函数外,还可以接受对象类型
对象方式是函数方式的简写方式 ???? ??如果新状态不依赖于原状态 ===> 使用对象方式 ???? ??如果新状态依赖于原状态 ===> 使用函数方式 ???? ??如果需要在setState()后获取最新的状态数据, 在第二个callback函数中读取 … 项目中,如果要更新state中数组元素,那还是新建一个数组
setState()更新状态是异步还是同步的?
1). 异步 OR 同步?
react相关回调中: 异步 ——生命周期回调、事件监听回调(不是直接使用原生dom的):利用事务来实现异步更新
其它异步回调中: 同步 ——定时器、dom事件监听回调、Promise对象(包括await 下面语句)
update2 = () => {
setTimeout(() => {
console.log('setTimeout setState()之前', this.state.count)
this.setState(state => ({count: state.count + 1}))
console.log('setTimeout setState()之后', this.state.count)
})
}
update4 = () => {
Promise.resolve().then(value => {
console.log('Promise setState()之前', this.state.count)
this.setState(state => ({ count: state.count + 1 }))
console.log('Promise setState()之后', this.state.count)
})
}
异步的setState() 多次调用, 如何处理? (新知识)
setState({}): 合并更新一次状态, 只调用一次render()更新界面 —状态更新和界面更新都合并了
setState(fn): 更新多次状态, 但只调用一次render()更新界面 —状态更新没有合并, 但界面更新合并了
合并更新的意思:以最后为准
使用对象形式
update6 = () => {
console.log('onclick setState()之前', this.state.count)
this.setState({ count: this.state.count + 1 })
console.log('onclick setState()之后', this.state.count)
console.log('onclick setState()之前2', this.state.count)
this.setState({ count: this.state.count + 5 })
console.log('onclick setState()之后2', this.state.count)
}
使用函数形式
update5 = () => {
console.log('onclick setState()之前', this.state.count)
this.setState(state => ({ count: state.count + 1 }))
console.log('onclick setState()之后', this.state.count)
console.log('onclick setState()之前2', this.state.count)
this.setState(state => ({ count: state.count + 5 }))
console.log('onclick setState()之后2', this.state.count)
}
交叉
update7 = () => {
console.log('onclick setState()之前2', this.state.count)
this.setState(state => ({ count: state.count + 50 }))
console.log('onclick setState()之后2', this.state.count)
console.log('onclick setState()之前', this.state.count)
this.setState({ count: this.state.count + 1 })
console.log('onclick setState()之后', this.state.count)
}
结果输出:
render() 1
如何得到异步更新后的状态数据?
在setState()的callback回调函数中
setState() 面试题
微任务 宏任务
JS中的宏任务和微任务的区别和用法 这篇文章介绍了为什么先promise后setTimeout。能应付做题,但完整的JavaScript线程机制,还需要阅读更详细的文章。
先理解同步任务和异步任务,(后面也会讲)
同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。 当指定的事情完成时,Event Table会将这个函数移入Event Queue。 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。 上述过程会不断重复,也就是常说的Event Loop(事件循环)。
除了广义的同步任务和异步任务,我们对任务有更精细的定义:
宏任务一般是:包括整体代码script,setTimeout,setInterval、I/O、UI render。
微任务主要是:Promise.then()、Object.observe、process.nextTick。
1.Promise在前,setTimeout在后
new Promise((resolve) => {
console.log('外层宏事件2');
resolve()
}).then(() => {
console.log('微事件1');
}).then(()=>{
console.log('微事件2')
})
console.log('外层宏事件1');
setTimeout(() => {
console.log('内层宏事件3')
}, 0)
外层宏事件2
外层宏事件1
微事件1
微事件2
内层宏事件3
2.setTimeout在前,Promise在后
setTimeout(() => {
console.log('内层宏事件3')
}, 0)
console.log('外层宏事件1');
new Promise((resolve) => {
console.log('外层宏事件2');
resolve()
}).then(() => {
console.log('微事件1');
}).then(()=>{
console.log('微事件2')
})
外层宏事件1
外层宏事件2
微事件1
微事件2
内层宏事件3
题目
注释中: 第一个输出 ==> 输出值
测试之后才知道:先render(), 后执行promise微任务
state = {
count: 0,
}
componentDidMount() {
this.setState({count: this.state.count + 1})
this.setState({count: this.state.count + 1})
console.log(this.state.count)
this.setState(state => ({count: state.count + 1}))
this.setState(state => ({count: state.count + 1}))
console.log(this.state.count)
setTimeout(() => {
this.setState({count: this.state.count + 1})
console.log('timeout', this.state.count)
this.setState({count: this.state.count + 1})
console.log('timeout', this.state.count)
}, 0)
Promise.resolve().then(value => {
this.setState({count: this.state.count + 1})
console.log('promise', this.state.count)
this.setState({count: this.state.count + 1})
console.log('promise', this.state.count)
})
}
render() {
const count = this.state.count
console.log('render', count)
Component存在的问题?
1). 父组件重新render(), 其子组件也会重新执行render(), 即使没有任何变化
2). 当前组件setState(), 重新执行render(), 即使state没有任何变化
1). 原因: 组件的componentShouldUpdate()默认返回true, 即使数据没有变化render()都会重新执行 2). 办法1: 重写shouldComponentUpdate(), 判断如果数据有变化返回true, 否则返回false 3). 办法2: 使用PureComponent代替Component 4). 说明: 一般都使用PureComponent来优化组件性能
但是使用PureComponent就强制规定,你不能只是修改对象的属性值,或者是数组里面的元素,而变量的引用没有变化。
线程概念
浏览器内核由很多模块组成:
dom树,Element对象,存储在内存中 一个<p> 标签,就要生成标签对应的对象
定时器
document.getElementById('btn').onclick = function () {
var start = Date.now()
console.log('启动定时器前...')
setTimeout(function () {
console.log('定时器执行了', Date.now()-start)
}, 200)
console.log('启动定时器后...')
for (var i = 0; i < 1000000000; i++) {
}
}
1. 定时器真是定时执行的吗?
- 定时器并不能保证真正定时执行
- 一般会延迟一丁点(可以接受), 也有可能延迟很长时间(不能接受)
2. 定时器回调函数是在分线程执行的吗?
3. 定时器是如何实现的?
- 代码的分类:
js引擎执行代码的基本流程 1.先执行初始化代码: 包含一些特别的代码 (定时器,绑定事件监听) 2.后面在某个时刻才会执行回调代码
如何证明js执行是单线程的? setTimeout()的回调函数是在主线程执行的
虽然在alert的等待确认过程中,新的浏览器是会计时的,但是回调函数依然要等待主线程代码执行完,再去执行回调函数。
setTimeout(function () {
console.log('timeout 2222')
alert('22222222')
}, 5000)
setTimeout(function () {
console.log('timeout 1111')
alert('1111111')
}, 4000)
setTimeout(function () {
console.log('timeout() 00000')
}, 0)
function fn() {
console.log('fn()')
}
fn()
console.log('alert()之前')
alert('------')
console.log('alert()之后')
Web Workers
主线程
<script type="text/javascript">
var input = document.getElementById('number')
document.getElementById('btn').onclick = function () {
var number = input.value
var worker = new Worker('worker.js')
worker.onmessage = function (event) {
console.log('主线程接收分线程返回的数据: '+event.data)
alert(event.data)
}
worker.postMessage(number)
console.log('主线程向分线程发送数据: '+number)
}
</script>
分线程
console.log(this)
this.onmessage = function (event) {
var number = event.data
console.log('分线程接收到主线程发送的数据: '+number)
var result = fibonacci(number)
postMessage(result)
console.log('分线程向主线程返回数据: '+result)
}
worker.js里的this不是执行window
postMessage发送消息 onmessage接收消息 这两个方法都是相对的。
|