setState() 官方文档
setState(updater, [callback])
参数一为带有形式参数的 updater 函数
updater 函数中接收的 state 和 props 都保证为最新。 updater 的返回值会与 state 进行浅合并。
setState(stateChange[, callback])
第一个参数除了接受函数外,还可以接受对象类型
对象方式是函数方式的简写方式
如果新状态不依赖于原状态 ===> 使用对象方式
如果新状态依赖于原状态 ===> 使用函数方式
如果需要在setState()后获取最新的状态数据, 在第二个callback函数中读取
…
项目中,如果要更新state中数组元素,那还是新建一个数组
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({}): 合并更新一次状态, 只调用一次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回调函数中
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}) //state=1
console.log(this.state.count) // 2 ==> 0
this.setState(state => ({count: state.count + 1})) //state=2
this.setState(state => ({count: state.count + 1})) //state=3
console.log(this.state.count) // 3 ==> 0
setTimeout(() => {
this.setState({count: this.state.count + 1})
console.log('timeout', this.state.count) // 10 ==> 6
this.setState({count: this.state.count + 1})
console.log('timeout', this.state.count) // 12 ==> 7
}, 0)
Promise.resolve().then(value => {
this.setState({count: this.state.count + 1}) //state=4
console.log('promise', this.state.count) // 6 ==>4
this.setState({count: this.state.count + 1}) //state=5
console.log('promise', this.state.count) // 8 ==> 5
})
}
render() {
const count = this.state.count
console.log('render', count) // 1 ==> 0 4 ==>3 5 ==>4 7 ==>5 9 ==>6 11 ==>7
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对象,存储在内存中
一个标签,就要生成标签对应的对象
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++) {
}
}
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('------') //暂停当前主线程的执行, 同时计时(可能因为H5的特性), 点击确定后, 恢复程序执行。
console.log('alert()之后')
主线程
<script type="text/javascript">
var input = document.getElementById('number')
document.getElementById('btn').onclick = function () {
var number = input.value
//创建一个Worker对象
var worker = new Worker('worker.js')
// 绑定接收消息的监听
worker.onmessage = function (event) {
console.log('主线程接收分线程返回的数据: '+event.data)
alert(event.data)
}
// 向分线程发送消息
worker.postMessage(number)
console.log('主线程向分线程发送数据: '+number)
}
// console.log(this) // window
</script>
分线程
console.log(this)
this.onmessage = function (event) {
var number = event.data
console.log('分线程接收到主线程发送的数据: '+number)
//计算
var result = fibonacci(number)
postMessage(result)
console.log('分线程向主线程返回数据: '+result)
// alert(result) alert是window的方法, 在分线程不能调用
// 分线程中的全局对象不再是window, 所以在分线程中不可能更新界面
}
postMessage发送消息
onmessage接收消息
这两个方法都是相对的。