上一小节我们处理了 setState
的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState
可以传入函数,同时 setState
可以传入第二参数作为回调函数。我们先把上一节的工作收个尾再介绍 ref
的实现。
完善 setState
立即执行
我们在代码中实现如下逻辑,传入函数,我们需要修改一下 getState
方法
this.setState((state) => ({ number: state.number 1 }));
this.setState((state) => ({ number: state.number 1 }));
// src/Component.js
getState() {
...
pendingStates.forEach(nextState => {
// 对参数进行判断
if (typeof nextState === 'function') {
nextState = nextState(state)// 这里把旧的 state 传入,立即执行,这样我们在代码中看到的就类似同步执行,函数返回新的 state 状态
}
state = {
...state,
...nextState
}
})
}
相信看完上面代码,大家应该能够理解为什么传入函数就可以同步执行。因为每次都会立即计算获取当前最新的状态。
实现回调函数
setState
的第二个参数会在页面更新后执行,获取到最新的状态返回值。所以我们能想到开始先把回调函数收集,在组件更新完成之后再依次执行,代码如下:
// src/Component.js Updater类
初始化需要定义回调函数栈
this.callbacks = []
在 addState 添加状态的同时收集回调函数
addState(partialState, callback) {
this.pendingStates.push(partialState)
callback && this.callbacks.push(callback)
...
}
在组件完成更新 updateComponent 方法的最后执行回调
updateComponent() {
const {classInstance, pendingStates, callbacks} = this
if (pendingStates.length) {
...
}
// 这里咱们使用个微任务执行,因为 js 的单线程机制,可以保证微任务一定在也买呢渲染后执行
queueMicrotask(() => {
callbacks.forEach(cb => cb.call(this))
this.callbacks.length = [] // 执行完成后清空
})
}
相信大家看过之前几节的基础上,能够很快的理解上面的代码,如果大家更这实现也基本能实现自己的 react-demo
了。
实现 createRef
我们通常用 ref
来获取真实 dom
节点然后进行操作,上一小节我们知道我们已经把真实的 dom
绑定在了 vdom
上,类组件的实例也能够拿到,所以我们只需加一层判断即可。
首先获取 ref
属性,入口文件中实现如下 dom
结构:
// src/index.js
constructor(props) {
super(props);
this.a = React.createRef(); // {current: null}
this.b = React.createRef();
this.result = React.createRef();
}
...
<>
<input ref={this.a} type="text" /> <input ref={this.b} type="text" />{" "}
<button onClick={this.handleClick}>=</button>{" "}
<input ref={this.result} type="text" />
</>
修改 react.js
文件
第一步返回值新增字段
const React = {
createElement,
Component,
createRef,
};
// 一行代码就完事了
function createRef() { return {current: null} }
我们还需要修改下返回的虚拟dom对象,新增 ref 属性
function createElement(type, config, chidlren) {
let ref
if (config) {
ref = config.ref
delete config.ref
...
}
...
return {
...
ref
}
}
我们注意 ref
对于 dom
来说一般用在类组件和正常的 dom
上,所以我们只需要修改 createDOM
和 mountClassComponent
方法
// src/react-dom.js
function createDOM(vdom) {
// 拿到 ref
const {type, props, ref} = vdom
...
if (ref) ref.current = dom
...
}
function mountClassComponent(vdom) {
// 同理
const {type, props, ref} = vdom
...
if (ref) ref.current = classInstance
...
}
本小节我们完善了 setState
方法的使用,了解了函数可以同步执行的原因;我们也知道了 ref
的实现原理,只需要对其 current
属性进行绑定即可。下一小节我们学习下 react
的生命周期。如果有不对,欢迎指正!