如果希望setState之后可以用上上一次的值,可以给setState传函数
handleClick = (syntheticEvent) => {
this.setState(state => ({ number: this.state.number + 1 }));
this.setState(state => ({ number: this.state.number + 1 }));
复制代码
setState什么是同步的 什么时候是异步的
-
在React能管辖的地方就是批量异步的,比如事件处理函数,比如生命周期函数异步
-
在React管不到的地方,就是同步的,例如setTimeout setInterval 原生事件处理函数
handleClick = (syntheticEvent) => {
setTimeout(() => {//在setTimeout是同步更新的
this.setState({ number: this.state.number + 1 });
console.log(this.state.number);
this.setState({ number: this.state.number + 1 });
console.log(this.state.number);
});
复制代码
批量更新的实现依赖于一个全局变量:
export let updateQueue = {
isBatchingUpdate =false,//默认值是非批量的,同步的
updaters: [],//更新器的数组
batchUpdate() {
for (let updater of updateQueue.updaters) {
updater.updateComponent();
}
updateQueue.updaters.length = 0;
updateQueue.isBatchingUpdate = false;
}
}
复制代码
在emitUpdate中需要增加判断,如果是批量更新,则将Updater对象都暂存在updateQueue.updaters中
class Updater {
constructor(classInstance) {
this.classInstance = classInstance;
this.pendingStates = [];//等待生效的数组
//this.callbacks = [];
}
addState(partialState) {
this.pendingStates.push(partialState);
this.emitUpdate();// 触发更新
}
emitUpdate() {
//有可能是批量异步更新,也有可能是同步更新
if (updateQueue.isBatchingUpdate) {//批量异步更新
updateQueue.updaters.push(this);//不刷 新组件视图了,只是把自己这个updater实例添加到updateQueue等待生效
} else {//同步直接更新
this.updateComponent();
}
}
复制代码
思路:在合成事件触发以前,将updateQueue.isBatchingUpdate改为true,批量更新完后再改回去
之前的版本中,要向button上绑定click事件
现在不向button上绑定事件了
而是把所有的事件都绑到document上
React17以前事件都委托到document上
React17之后事件都委托到容器上 <divid=”container”>
为什么 是因为在一个页面中可以存在多个不同的React应用
<divid=”root1″> ReactDOM.render(
,root1);
<divid=”root2″>ReactDOM.render(
,root2);
相当于一个事件委托
1.可以实现刚才说的,事件开始
//updateQueue.isBatchingUpdate = true;
结束 的时候
//updateQueue.batchUpdate();
2.可以做一些浏览器兼容性处理
不同浏览器API不一样的
把不同的事件对象做成一个标准化的事件对象,提供标准的API访问供用户使用。
需要更新updateProps
function updateProps(dom, oldProps, newProps) {
for (let key in newProps) {
if (key === 'children') {//children
continue;//此处忽略子节点的处理
} else if (key === 'style') {//style
let styleObj = newProps[key];
for (let attr in styleObj) {
dom.style[attr] = styleObj[attr];
}
} else if (key.startsWith('on')) {
//dom[key.toLocaleLowerCase()] = newProps[key];
addEvent(dom, key.toLocaleLowerCase(), newProps[key]);
复制代码
新建一个event.js文件,专门用来存储事件相关的方法:
import { updateQueue } from './Component';
function addEvent(dom, eventType, eventHandler) {
let store;
if (dom._store) {
store = dom._store;
} else {
dom._store = {};
store = dom._store;
}
//store.onclick=handleClick
store[eventType] = eventHandler;
//document.onclick=dispatchEvent
if (!document[eventType]) {
document[eventType] = dispatchEvent;
}
}
/**
* 不管点什么按钮,触发什么事件,最终执行的都是dispatchEvent
* 在合成事件的处理函数里,状态的更新是批量的
* @param {*} event 原生的事件对象 不同的浏览可能是不一样
*/
function dispatchEvent(event) {
//target =button type ==click
let { target, type } = event;
let eventType = 'on' + type;
//先把批量更新 全局变量设置为true
updateQueue.isBatchingUpdate = true;
let syntheticEvent = createSyntheticEvent(event);
//获取事件源DOM对象上的store属性
let { store } = target;
let eventHandler = store && store[eventType];//handleClick
eventHandler && eventHandler.call(target, syntheticEvent);//handleClick(syntheticEvent);
updateQueue.isBatchingUpdate = false;
updateQueue.batchUpdate();//进行真正的更新
}
function createSyntheticEvent(nativeEvent) {
let syntheticEvent = { nativeEvent };
for (let key in nativeEvent) {
syntheticEvent[key] = nativeEvent[key];
}
//此处会有一些兼容性处理
return syntheticEvent;
}
复制代码
流程:
用户点击onClick handleClick -> dispatchEvent
-> handleClick(syntheticEvent) -> setState -> updater.addState -> Updater.emitUpdate -> updateQueue.updaters.push(Updater)
-> updateQueue.batchUpdate