Comments (34)
恕我直言, 这其实就是react的bug, 整出这么大一堆概念, 就是想让你们接受这个bug, 不要踩这个坑
from daily-interview-question.
我理解的是:isBatchingUpdates 默认值为 false,当 react 自身的事件处理函数或 react 生命周期触发时,isBatchingUpdates 会被赋值为 true,当更新完成时又会被复原为 false。 @code-coder
from daily-interview-question.
@azl397985856 在React的setState函数实现中,会根据一个变量 isBatchingUpdate 来判断是直接同步更新this.state还是放到队列中异步更新 。React使用了事务的机制,React的每个生命周期和合成事件都处在一个大的事务当中。在事务的前置钩子中调用batchedUpdates方法修改isBatchingUpdates变量为true,在后置钩子中将变量置为false。原生绑定事件和setTimeout异步的函数没有进入到React的事务当中,或者当他们执行时,刚刚的事务已近结束了,后置钩子触发了,所以此时的setState会直接进入非批量更新模式,表现在我们看来成为了同步SetState。
from daily-interview-question.
具体可以见 ReactFiberScheduler 中的 performWork 和 performSyncWork。 楼主说的基本是对的,如果有这种 async 的 work 就不执行 batch update 如果没有 async 的就执行 batch update,setTimeout 和 promise 这些要进入 EventLoop 队列的都会被认为是 async work。
from daily-interview-question.
我打印的 0 0 1 1
from daily-interview-question.
对于 非concurrent 模式 答案 0023 屏幕显示 0 1 2 3
-
组件创建 屏幕显示 val 为 0
-
componentDidMount 执行
注意:(对于前两次之所以都是0 , 并不是 因为 isBatchingUpdates 为 true, 而是还在处理渲染状态,(只有事件系统等触发的才会isBatchingUpdates设置为true)所谓的渲染状态 也就是源码 isRendering 是 true , 这个变量是 虚拟DOM构建过程以及commit过程中都会维持 true 的状态。)
(1)第一个setState触发,创建一个 调度任务,此时 调度链表中有 初始化创建的调度任务和这一次的调度任务。这次调度的过期时间因为是 非concurrent,所以是 sync。将 {val: 1} 对象放入到调度的updateQueue中。此时并不会马上执行,因为 第一个创建的调度还没有结束 isRendering 还是true。所以不会进入渲染阶段,屏幕还是 0.
(2)然后打印 0.
(3)第二个setState触发,创建一个调度任务, 此时的调度链表中有三个调度任务,分别为 初始化的时候调度、第一次setSate、第二次setSate. 这次调度的过期时间因为是 非concurrent,所以是 sync。将 {val: 1} 对象放入到调度的updateQueue中。同理 isRendering 还是 true,屏幕显示还是0.
(4) 然后打印0.
(5)初始化结束开始,isRendering 设置成 false。第一个初始化调度从调度链表中清除,开始进入处理剩下调度任务过程, 然后由于两次的调度任务一致, 所以,优先级一样,先处理第一次的调度任务,由于 第一次 setState 与第二次 优先级都一样, 所以在 render 阶段 收集 更新的时候都被处理了。
(6)屏幕显示 1(并不是 两次 setState 都会输出,而是由于过期时间都一样在 render 的收集更新的时候被处理调了) -
setTime 执行
(1)由于此时 isRendering 是false, 所以不会批量更新,而是第三个 setState 调用的时候,过期时间为 sync。 然后创建调度,然后直接进入更新。
(2) 打印输出2. 屏幕显示 2
(3)同理,第四个setState 同步执行
(4)打印输出 3, 屏幕显示 3
对于 concurrent 模式 正常60hz刷新率情况下 答案 0011 屏幕显示 1 2
-
组件创建,进入调度执行,因为第一个创建的调度任务 肯定是在一帧空闲时间内执行的, 但是可能在 commit 阶段 检测到没有剩余时间了, 所以不会绘制到屏幕上。此时 isRendering 还是true
-
componentDidMount 执行
(1)执行第一个 setState, 此时 isRendering 是 true,仅仅是创建 调度任务 过期时间是一个异步的时间戳。并不会执行。
(2)打印出 0
(3)执行第二个 setState, 此时 isRedndering是true, 也是不会执行, 仅仅市创建调度任务, 注意的是,这里的异步过期时间与第一次的相同(因为 都是在一个 rendering 中)。
(4)打印出 0
(5)此时调度到第二个setState 的 调度任务的时候, 调度时间早已经过期了,所以退化成 同步的方式直接执行,屏幕绘制出 1。 -
setTimeout 执行
(1)第三个setState,进入到下一个时间循环的时候,在这之前可能执行了一次 state 变更,变成了1 所以 第三个创建调度任务的时候 val 可能是 2(只能说是可能, 因为不同的刷新率 和 组件大小都会影响了上一次的setState, 但是在目前实例仅仅有一个组件的情况下,state 肯定变成了 1 的因为必然执行了 render 过程)
(2)打印出 1(也是可能值,只不过在 60hz 刷新率下 1 的概率最大)
(3)第四个setState,创建调度任务,此调度任务放入队列,该过期时间可能与第三次的过期时间相同,因为异步计算时间都是在50ms 内的都作为一个过期时间。因为 两个过期时间一致, 所有中断了第三次的更新,进而更新第四次的。
(4)打印出 1
(5)屏幕显示 2
from daily-interview-question.
题目呢???
from daily-interview-question.
react-hooks时代貌似改写了这种方式,我用react-hooks写法写出来的结果都是0
from daily-interview-question.
react-hooks时代貌似改写了这种方式,我用react-hooks写法写出来的结果都是0
更新的方式没有更改,首先是因为useEffect函数只运行了一次,其次setTimeout是个闭包,访问到的值一直是0(按照正常的写法setVal(val+1))。以例子来看的话,并没有执行更新的操作。
具体可以参考 issue讨论: facebook/react#14010
from daily-interview-question.
在16.8.6源码中调试来看,这题和批量更新是没有关系的,通常批量更新是发生在react事件机制中即触发了事件回调会设置批量更新的标志。从源码中看,该题目的解释是:当前已经处于渲染的阶段,在这个过程中,框架会主动去合并在这期间触发的更新。
react的渲染主要包含两个阶段,render阶段和commit阶段,这两个阶段的包含在performWorkOnRoot方法中,该方法设置isRendering变量,表示当前是否处于渲染阶段。
from daily-interview-question.
现在考这题已经没有意义了。。React 18默认并发就是0011,18以下就是0023。。
from daily-interview-question.
为什么不是 0 0 3 4
我刚开始得到结果也是0034,看楼主的解答,是前两次setState
触发了批量更新,两次setState
合并成了一次.
from daily-interview-question.
1、第一次和第二次都是在 react 自身生命周期内,触发时 isBatchingUpdates 为 true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,所以打印时获取的都是更新前的状态 0。
2、两次 setState 时,获取到 this.state.val 都是 0,所以执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次。设置完成后 state.val 值为 1。
3、setTimeout 中的代码,触发时 isBatchingUpdates 为 false,所以能够直接进行更新,所以连着输出 2,3。
输出: 0 0 2 3
请问一下大佬,isBatchingUpdates的判断条件是什么呀?
from daily-interview-question.
当前函数式组件当中通过useState去定义state然后通过setState的回调函数的方式+1,react
输出为0,0,0,0。
是否跟当前的Concurrent mode 有关联,或者说0,0,2,3这种情况只会在生命周期当中实现并且react v<18
from daily-interview-question.
1、第一次和第二次都是在 react 自身生命周期内,触发时 isBatchingUpdates 为 true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,所以打印时获取的都是更新前的状态 0。
2、两次 setState 时,获取到 this.state.val 都是 0,所以执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次。设置完成后 state.val 值为 1。
3、setTimeout 中的代码,触发时 isBatchingUpdates 为 false,所以能够直接进行更新,所以连着输出 2,3。
输出: 0 0 2 3请问一下大佬,isBatchingUpdates的判断条件是什么呀?
from daily-interview-question.
哦也我做对了
from daily-interview-question.
很好理解的问题 正常情况下 react是会同步更新数据的 也就是说isBatchingUpdate触发变成true 但是 如果你将state的状态值加入到setTimeout的延迟处理队列中 打印出来的值就是setState之后的值,或者你同时调用setState的第二个参数 也会做到异步操作 获取设置之后的state value
from daily-interview-question.
做成了 0022 哎。竟然有isBatchingUpdates这种东西 学习了 @yhlben 。
from daily-interview-question.
react-hooks时代貌似改写了这种方式,我用react-hooks写法写出来的结果都是0
更新的方式没有更改,首先是因为useEffect函数只运行了一次,其次setTimeout是个闭包,访问到的值一直是0(按照正常的写法setVal(val+1))。以例子来看的话,并没有执行更新的操作。
具体可以参考 issue讨论: facebook/react#14010
应该是setVal((val) => val+1)
from daily-interview-question.
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
from daily-interview-question.
为什么不是 0 0 3 4
from daily-interview-question.
react的state是为view服务的,state到view是响应式的,这种代码看瞎眼
from daily-interview-question.
react的setState可以认为是微任务的一次执行,但是我有点不太明白,setTimeout内的setState为什么没有批量更新?
from daily-interview-question.
我打印的 0 0 1 1
因为你开启了 concurrent 模式 见我的回复 👆
from daily-interview-question.
为什么不是 0 0 3 4
虽然说是批量更新,但是两者的值指向同一个元素,因此后面的覆盖前面的
from daily-interview-question.
@wangmaoshu 那你的意思是说 如果是事件,比如点击的时候结果会不一样么?我用点击事件,做这个操作和用 didmount执行,都是一样的结果
from daily-interview-question.
react v17.0.0 0 0 2 3
from daily-interview-question.
import React from 'react';
class Exam19 extends React.Component {
constructor(props: any) {
super(props);
console.log('Exam19-1');
this.state = {
val: 0
};
this.log1 = 0;
this.log2 = 0;
this.log3 = 0;
this.log4 = 0;
}
componentDidMount() {
this.setState({val: this.state.val + 1});
this.log1 = this.state.val;
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
this.log2 = this.state.val;
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
this.log3 = this.state.val;
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
this.log4 = this.state.val;
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return <>
<div>{this.log1}</div>
<div>{this.log2}</div>
<div>{this.log3}</div>
<div>{this.log4}</div>
</>;
}
};
export default Exam19;
我在跑代码时发现,命令行打印是 0 0 2 3,然后我给代码加了this.logx
想在页面展示出数据,结果页面展示是0 0 2 0
from daily-interview-question.
两个变式
输出:1234
from daily-interview-question.
批处理机制:setstate不会立即更新state,而是将更新数据放入队列,在事件循环结束统一更新,提高效率
取决于setstate是否使用批处理机制,在事件监听函数和生命周期函数中异步更新,在setTimeout中同步更新;不是绝对固定,react会调整策略
from daily-interview-question.
import React from 'react'; class Exam19 extends React.Component { constructor(props: any) { super(props); console.log('Exam19-1'); this.state = { val: 0 }; this.log1 = 0; this.log2 = 0; this.log3 = 0; this.log4 = 0; } componentDidMount() { this.setState({val: this.state.val + 1}); this.log1 = this.state.val; console.log(this.state.val); // 第 1 次 log this.setState({val: this.state.val + 1}); this.log2 = this.state.val; console.log(this.state.val); // 第 2 次 log setTimeout(() => { this.setState({val: this.state.val + 1}); this.log3 = this.state.val; console.log(this.state.val); // 第 3 次 log this.setState({val: this.state.val + 1}); this.log4 = this.state.val; console.log(this.state.val); // 第 4 次 log }, 0); } render() { return <> <div>{this.log1}</div> <div>{this.log2}</div> <div>{this.log3}</div> <div>{this.log4}</div> </>; } }; export default Exam19;我在跑代码时发现,命令行打印是 0 0 2 3,然后我给代码加了this.logx 想在页面展示出数据,结果页面展示是0 0 2 0
this.setState({val: this.state.val + 1}); 执行setState方法后会更新视图,所以最新的this.log3 的值会随之更新到页面。
this.log4 = this.state.val; 这种赋值方式并不会更新视图。
若是后面再执行setState方法,页面中的log4的值会随之更新。
from daily-interview-question.
现在考这题已经没有意义了。。React 18默认并发就是0011,18以下就是0023。。
当前线上绝大多数还是18以下吧
from daily-interview-question.
题
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
答
0 // react 的可控范围内 ,批量更新
0 // react 的可控范围内 ,批量更新
// 只更新了 一次 state =1
2 // react 不可控范围,同步更新 state+1 -> 2
3 // react 不可控范围,同步更新 state+1 -> 3
解释:#17
from daily-interview-question.
这个在react的Concurrent mode下打印的是0,0,1,1,在Sync mode下打印的是0,0,2,3
from daily-interview-question.
Related Issues (20)
- ``` javascript HOT 2
- 反转数字字符串 HOT 2
- es11
- 实现
- [...new Set(arr.toString().split(',').sort((a,b) => a-b ))].map(item => Number(item))
- let nums1 = [3,3,2] let nums2 = [1,2,3,3,2,2,4444] function res (nums1,nums2){ let a = [] for (const item of nums1) { for (const i in nums2) { if(item==nums2[i]){ a.push(nums2[i]) nums2.splice(i,1) break } } }; return a } console.log(res(nums1,nums2))
- splice插入
- 自己回答一波,若有错误请各位指出,互相学习!谢谢 ~
- 递归
- 第226题:TS 中逆变和协变如何理解
- objmap
- 通过协程来实现
- 这代码写的...
- 第 161 题:用最精炼的代码实现数组非零非负最小值 index HOT 2
- No description provided.
- > 就我的使用来说(Vue)key的作用是为了在数据变化时强制更新组件,以避免“原地复用”带来的副作用,官方文档也说明了**不带key性能更好**,见:[List Rendering - key](https://cn.vuejs.org/v2/guide/list.html#key)
- 解释原因
- an
- 同步/异步 > 微任务>宏任务
- Object.prototype,map
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from daily-interview-question.