看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/...)

  • 从 0 到 1 实现 React 系列 —— JSX 和 Virtual DOM
  • 从 0 到 1 实现 React 系列 —— 组件和 state|props
  • 从 0 到 1 实现 React 系列 —— 生命周期和 diff 算法
  • 从 0 到 1 实现 React 系列 —— 优化 setState 和 ref 的实现

同步 setState 的问题

而在现有 setState 逻辑实现中,每调用一次 setState 就会执行 render 一次。因此在如下代码中,每次点击增加按钮,因为 click 方法里调用了 10 次 setState 函数,页面也会被渲染 10 次。而我们希望的是每点击一次增加按钮只执行 render 函数一次。

export default class B extends Component {constructor(props) {super(props)this.state = {count: 0}this.click = this.click.bind(this)}click() {for (let i = 0; i < 10; i++) {this.setState({ // 在先前的逻辑中,没调用一次 setState 就会 render 一次count: ++this.state.count})}}render() {console.log(this.state.count)return (<div><button onClick={this.click}>增加</button><div>{this.state.count}</div></div>)}
}

异步调用 setState

查阅 setState 的 api,其形式如下:

setState(updater, [callback])

它能接收两个参数,其中第一个参数 updater 可以为对象或者为函数 ((prevState, props) => stateChange),第二个参数为回调函数;

确定优化思路为:将多次 setState 后跟着的值进行浅合并,并借助事件循环等所有值合并好之后再进行渲染界面。

let componentArr = []// 异步渲染
function asyncRender(updater, component, cb) {if (componentArr.length === 0) {defer(() => render())       // 利用事件循环,延迟渲染函数的调用}if (cb) defer(cb)             // 调用回调函数if (_.isFunction(updater)) {  // 处理 setState 后跟函数的情况updater = updater(component.state, component.props)}// 浅合并逻辑component.state = Object.assign({}, component.state, updater)if (componentArr.includes(component)) {component.state = Object.assign({}, component.state, updater)} else {componentArr.push(component)}
}function render() {let componentwhile (component = componentArr.shift()) {renderComponent(component) // rerender}
}// 事件循环,关于 promise 的事件循环和 setTimeout 的事件循环后续会单独写篇文章。
const defer = function(fn) {return Promise.resolve().then(() => fn())
}

此时,每点击一次增加按钮 render 函数只执行一次了。

ref 的实现

在 react 中并不建议使用 ref 属性,而应该尽量使用状态提升,但是 react 还是提供了 ref 属性赋予了开发者操作 dom 的能力,react 的 ref 有 stringcallbackcreateRef 三种形式,分别如下:

// string 这种写法未来会被抛弃
class MyComponent extends Component {componentDidMount() {this.refs.myRef.focus()}render() {return <input ref="myRef" />}
}// callback(比较通用)
class MyComponent extends Component {componentDidMount() {this.myRef.focus()}render() {return <input ref={(ele) => {this.myRef = ele}} />}
}// react 16.3 增加,其它 react-like 框架还没有同步
class MyComponent extends Component {constructor() {super() {this.myRef = React.createRef()}}componentDidMount() {this.myRef.current.focus()}render() {return <input ref={this.myRef} />}
}

React ref 的前世今生 罗列了三种写法的差异,下面对上述例子中的第二种写法(比较通用)进行实现。

首先在 setAttribute 方法内补充上对 ref 的属性进行特殊处理,

function setAttribute(dom, attr, value) {...else if (attr === 'ref') {          // 处理 ref 属性if (_.isFunction(value)) {value(dom)}}...
}

针对这个例子中 this.myRef.focus() 的 focus 属性需要异步处理,因为调用 componentDidMount 的时候,界面上还未添加 dom 元素。处理 renderComponent 函数:

function renderComponent(component) {...else if (component && component.componentDidMount) {defer(component.componentDidMount.bind(component))}...
}

刷新页面,可以发现 input 框已为选中状态。

处理完普通元素的 ref 后,再来处理下自定义组件的 ref 的情况。之前默认自定义组件上是没属性的,现在只要针对自定义组件的 ref 属性做相应处理即可。稍微修改 vdomToDom 函数如下:

function vdomToDom(vdom) {if (_.isFunction(vdom.nodeName)) { // 此时是自定义组件...for (const attr in vdom.attributes) { // 处理自定义组件的 ref 属性if (attr === 'ref' && _.isFunction(vdom.attributes[attr])) {vdom.attributes[attr](component)}}...}...
}

跑如下测试用例:

class A extends Component {constructor() {super()this.state = {count: 0}this.click = this.click.bind(this)}click() {this.setState({count: ++this.state.count})}render() {return <div>{this.state.count}</div>}
}class B extends Component {constructor() {super()this.click = this.click.bind(this)}click() {this.A.click()}render() {return (<div><button onClick={this.click}>加1</button><A ref={(e) => { this.A = e }} /></div>)}
}

效果如下:

项目地址,关于如何 pr

本系列文章拜读和借鉴了 simple-react,在此特别感谢 Jiulong Hu 的分享。

转载于:https://www.cnblogs.com/MuYunyun/p/9427911.html

从 0 到 1 实现 React 系列 —— 4.setState优化和ref的实现相关推荐

  1. 从 0 到 1 实现 React 系列 —— 2.组件和 state|props

    看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/. ...

  2. 从 0 到 1 实现 React 系列 —— 1.JSX 和 Virtual DOM

    看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/. ...

  3. 从 0 到 1 实现 React 系列 —— 4.优化setState和ref的实现

    看源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/生命周期/diff算法/setState/ref/. ...

  4. 从 0 到 1 实现 React 系列 —— 组件和 state|props

    阅读源码一个痛处是会陷进理不顺主干的困局中,本系列文章在实现一个 (x)react 的同时理顺 React 框架的主干内容(JSX/虚拟DOM/组件/...) 组件即函数 在上一篇 JSX 和 Vir ...

  5. dispatch作用 react_「React系列」手把手带你撸后台系统(Redux与路由鉴权)

    [React系列]手把手带你撸后台系统(Redux与路由鉴权) 来源:https://juejin.im/post/5d9b5ddee51d45781b63b8f7 上一篇我们介绍了系统架构,这一篇将 ...

  6. React 系列之基础二(状态管理)

    React 系列之基础二 React 状态管理 1.Flux 架构与 Redux 在 Flux 中, 状态完全从 React-components 分离到自己的存储中. 存储中的状态不会直接更改, 而 ...

  7. VMware View 5.0从菜鸟到高手系列 10 –远程图形工作站配置篇

    本文档依照PCoIP设备供应商丽台(Leadtek)提供的零客户机VP200P以及PCoI卡VP200H为例,为了介绍整个安装步骤.以下配置在VMware view 4.5中安装,但在更新的版本中如V ...

  8. React Native 0.59.0 发布,使用 React 编写原生应用

    React Native 0.59.0 发布了.React Native 使开发者只使用 JavaScript 也能编写原生移动应用. 新版更新亮点: React Hooks React Native ...

  9. 用SignalR 2.0开发客服系统[系列3:实现点对点通讯]

    用SignalR 2.0开发客服系统[系列3:实现点对点通讯] 原文:用SignalR 2.0开发客服系统[系列3:实现点对点通讯] 前言 目录: 用SignalR 2.0开发客服系统[系列1:实现群 ...

最新文章

  1. VSCode中屏蔽文件files.exclude和屏蔽文件搜索search.exclude
  2. 河南计算机程序大赛,我院成功举办河南省第十一届ACM大学生程序设计竞赛
  3. tcpip路由技术卷一_减少与开发的撕战,结合容器化技术轻松重构运维平台
  4. c语言编译器怎么用scanfkl,C语言一些笔记
  5. hibernate.cfg.xml ,hibernate.properties 关系
  6. 代码精进之路读后感(三)
  7. Scott Mitchell 的ASP.NET 2.0数据教程之十七:: 研究插入、更新和删除的关联事件
  8. excel制作甘特图模板
  9. 【免费分享】全新DHPST分销系统/YEP分销/云主机分销系统源码
  10. 【持续更新】MARL 算法汇总
  11. 分布式架构——Gossip 协议详解
  12. 虚拟内存设置在其他盘引发的问题(待解决)
  13. 学历不高的人,去学这5个技术,好找工作,上班也不累
  14. 基于STM32F0实现人体红外传感器
  15. 自考02324离散数学第二章思维导图
  16. ubuntu更新软件——终端命令(更新所有软件[可更新])
  17. Revit平面视图控制
  18. 无极性电容和有极性电容的爆炸
  19. efm32芯片电压_谁说壁虎没用?用efm32做个USB电压电流表(可诱导QC2.0)
  20. WPF 最简单的TextBox水印

热门文章

  1. Serverless Live | 9 大场景带你从零实践 Serverless
  2. “数据驱动”时代来临,阿里云数据中台如何赋能金融业?(附重磅报告下载)
  3. 如何解决大规模机器学习的三大痛点?
  4. java同步锁如何使用_java 同步锁(synchronized)的正确使用姿势
  5. python数据科学讲解_数据科学的概念-Python数据科学技术详解与商业项目实战精讲 - Python学习网...
  6. QLattice:你不知道的新的机器学习模型
  7. For the king:出色的冒险,失败的角色扮演
  8. PMP每日三题2022年2月11日
  9. THUPCCTSAPIO摸鱼被$\Huge{\color{black}{\mathbf{z}}\color{red}{\mathbf{zh}}}$爆踩记
  10. 网易严选的wkwebview测试之路