日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時間:2024/6/30 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 从 0 到 1 实现 React 系列 —— 4.setState优化和ref的实现 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

看源碼一個痛處是會陷進(jìn)理不順主干的困局中,本系列文章在實現(xiàn)一個 (x)react 的同時理順 React 框架的主干內(nèi)容(JSX/虛擬DOM/組件/生命周期/diff算法/setState/ref/...)

  • 從 0 到 1 實現(xiàn) React 系列 —— JSX 和 Virtual DOM
  • 從 0 到 1 實現(xiàn) React 系列 —— 組件和 state|props
  • 從 0 到 1 實現(xiàn) React 系列 —— 生命周期和 diff 算法
  • 從 0 到 1 實現(xiàn) React 系列 —— 優(yōu)化 setState 和 ref 的實現(xiàn)

同步 setState 的問題

而在現(xiàn)有 setState 邏輯實現(xiàn)中,每調(diào)用一次 setState 就會執(zhí)行 render 一次。因此在如下代碼中,每次點擊增加按鈕,因為 click 方法里調(diào)用了 10 次 setState 函數(shù),頁面也會被渲染 10 次。而我們希望的是每點擊一次增加按鈕只執(zhí)行 render 函數(shù)一次。

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({ // 在先前的邏輯中,沒調(diào)用一次 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>)} }

異步調(diào)用 setState

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

setState(updater, [callback])

它能接收兩個參數(shù),其中第一個參數(shù) updater 可以為對象或者為函數(shù) ((prevState, props) => stateChange),第二個參數(shù)為回調(diào)函數(shù);

確定優(yōu)化思路為:將多次 setState 后跟著的值進(jìn)行淺合并,并借助事件循環(huán)等所有值合并好之后再進(jìn)行渲染界面。

let componentArr = []// 異步渲染 function asyncRender(updater, component, cb) {if (componentArr.length === 0) {defer(() => render()) // 利用事件循環(huán),延遲渲染函數(shù)的調(diào)用}if (cb) defer(cb) // 調(diào)用回調(diào)函數(shù)if (_.isFunction(updater)) { // 處理 setState 后跟函數(shù)的情況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} }// 事件循環(huán),關(guān)于 promise 的事件循環(huán)和 setTimeout 的事件循環(huán)后續(xù)會單獨寫篇文章。 const defer = function(fn) {return Promise.resolve().then(() => fn()) }

此時,每點擊一次增加按鈕 render 函數(shù)只執(zhí)行一次了。

ref 的實現(xiàn)

在 react 中并不建議使用 ref 屬性,而應(yīng)該盡量使用狀態(tài)提升,但是 react 還是提供了 ref 屬性賦予了開發(fā)者操作 dom 的能力,react 的 ref 有 string、callback、createRef 三種形式,分別如下:

// 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 的前世今生 羅列了三種寫法的差異,下面對上述例子中的第二種寫法(比較通用)進(jìn)行實現(xiàn)。

首先在 setAttribute 方法內(nèi)補(bǔ)充上對 ref 的屬性進(jìn)行特殊處理,

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

針對這個例子中 this.myRef.focus() 的 focus 屬性需要異步處理,因為調(diào)用 componentDidMount 的時候,界面上還未添加 dom 元素。處理 renderComponent 函數(shù):

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

刷新頁面,可以發(fā)現(xiàn) input 框已為選中狀態(tài)。

處理完普通元素的 ref 后,再來處理下自定義組件的 ref 的情況。之前默認(rèn)自定義組件上是沒屬性的,現(xiàn)在只要針對自定義組件的 ref 屬性做相應(yīng)處理即可。稍微修改 vdomToDom 函數(shù)如下:

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>)} }

效果如下:

項目地址,關(guān)于如何 pr

本系列文章拜讀和借鑒了 simple-react,在此特別感謝 Jiulong Hu 的分享。

轉(zhuǎn)載于:https://www.cnblogs.com/MuYunyun/p/9427911.html

總結(jié)

以上是生活随笔為你收集整理的从 0 到 1 实现 React 系列 —— 4.setState优化和ref的实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。