摘要:本文翻譯自原作者如果有任何版權(quán)問(wèn)題,請(qǐng)聯(lián)系當(dāng)你在組件中調(diào)用時(shí),你覺得會(huì)發(fā)生什么當(dāng)然,會(huì)用這條狀態(tài)重新渲染組件并且更新匹配到的,然后返回元素。如果你之前使用過(guò)一些渲染器比如說(shuō),你可能知道在頁(yè)面中使用超過(guò)一個(gè)渲染器是沒什么問(wèn)題的。
本文翻譯自:How Does setState Know What to Do?原作者:Dan Abramov
如果有任何版權(quán)問(wèn)題,請(qǐng)聯(lián)系[email protected]
當(dāng)你在組件中調(diào)用setState時(shí),你覺得會(huì)發(fā)生什么?
import React from "react"; import ReactDOM from "react-dom"; class Button extends React.Component { constructor(props) { super(props); this.state = { clicked: false }; this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState({ clicked: true }); } render() { if (this.state.clicked) { returnThanks
; } return ( ); } } ReactDOM.render(, document.getElementById("container"));
當(dāng)然,React會(huì)用{ clicked: true} 這條狀態(tài)重新渲染組件并且更新匹配到的DOM,然后返回Thanks
元素。
聽起來(lái)似乎簡(jiǎn)潔明了。但別急,React(或者說(shuō)React DOM)是怎么做的?
更新DOM聽起來(lái)像是React DOM的事兒,但別忘了我們調(diào)用的可是this.setState(),它是React的東西,可不是React DOM的。另外,我們的基類React.Component是被定義在React內(nèi)部。
所以問(wèn)題來(lái)了:React.Component內(nèi)部的setState怎么能去更新DOM呢?
事先聲明:就像我的其他博客,你不需要熟練掌握React。這篇博客是為那些想要看看面紗之后是什么東西的人準(zhǔn)備的。完全可選!
我們或許會(huì)認(rèn)為React.Component類已經(jīng)包含了DOM更新邏輯。
但如果這是事實(shí),那this.setState是如何工作在其他環(huán)境中呢?比如:在React Native App中的組件也能繼承React.Component,他們也能像上面一樣調(diào)用this.setState(),并且React Native工作在Android和iOS的原生視圖而不是DOM中。
你可能也對(duì)React Test Renderer 或 Shallow Renderer比較熟悉。這兩個(gè)測(cè)試渲染器讓你可以渲染一般的組件并且也能在他們中調(diào)用this.setState,但他們可都不使用DOM。
如果你之前使用過(guò)一些渲染器比如說(shuō)React ART,你可能知道在頁(yè)面中使用超過(guò)一個(gè)渲染器是沒什么問(wèn)題的。(比如:ART組件工作在React DOM 樹的內(nèi)部。)這會(huì)產(chǎn)生一個(gè)不可維持的全局標(biāo)志或變量。
所以React.Component以某種方式將state的更新委托為具體的平臺(tái)(譯者注:比如Android, iOS),在我們理解這是如何發(fā)生之前,讓我們對(duì)包是如何被分離和其原因挖得更深一點(diǎn)吧!
這有一個(gè)常見的錯(cuò)誤理解:React "引擎"在react包的內(nèi)部。這不是事實(shí)。
事實(shí)上,從 React 0.14開始對(duì)包進(jìn)行分割時(shí),React包就有意地僅導(dǎo)出關(guān)于如何定義組件的API了。React的大部分實(shí)現(xiàn)其實(shí)在“渲染器”中。
渲染器的其中一些例子包括:react-dom,react-dom/server,react-native,react-test-renderer,react-art(另外,你也可以構(gòu)建自己的)。
這就是為什么react包幫助很大而不管作用在什么平臺(tái)上。所有它導(dǎo)出的模塊,比如React.Component,React.createElement,React.Children和[Hooks](https://reactjs.org/docs/hooks-intro.html),都是平臺(tái)無(wú)關(guān)的。無(wú)論你的代碼運(yùn)行在React DOM、React DOM Server、還是React Native,你的組件都可以以一種相同的方式導(dǎo)入并且使用它們。
與之相對(duì)的是,渲染器會(huì)暴露出平臺(tái)相關(guān)的接口,比如ReactDOM.render(),它會(huì)讓你可以把React掛載在DOM節(jié)點(diǎn)中。每個(gè)渲染器都提供像這樣的接口,但理想情況是:大多數(shù)組件都不需要從渲染器中導(dǎo)入任何東西。這能使它們更精簡(jiǎn)。
大多數(shù)人都認(rèn)為React“引擎”是位于每個(gè)獨(dú)立的渲染器中的。許多渲染器都包含一份相同的代碼—我們叫它“調(diào)節(jié)器”,為了表現(xiàn)的更好,遵循這個(gè)步驟 可以讓調(diào)節(jié)器的代碼和渲染器的代碼在打包時(shí)歸到一處。(拷貝代碼通常不是優(yōu)化“打包后文件”(bundle)體積的好辦法,但大多數(shù)React的使用者一次只需要一個(gè)渲染器,比如:react-dom(譯者注:因此可以忽略調(diào)節(jié)器的存在))
The takeaway here 是react包僅僅讓你知道如何使用React的特性而無(wú)需了解他們是如何被實(shí)現(xiàn)的。渲染器(react-dom,react-native等等)會(huì)提供React特性的實(shí)現(xiàn)和平臺(tái)相關(guān)的邏輯;一些關(guān)于調(diào)節(jié)器的代碼被分享出來(lái)了,但那只是多帶帶渲染器的實(shí)現(xiàn)細(xì)節(jié)而已。
現(xiàn)在我們知道了為什么react和react-dom包需要為新特定更新代碼了。比如:當(dāng)React16.3新增了Context接口時(shí),React.createContext()方法會(huì)在React包中被暴露出來(lái)。
但是React.createContext()實(shí)際上不會(huì)實(shí)現(xiàn)具體的邏輯(譯者注:只定義接口,由其他渲染器來(lái)實(shí)現(xiàn)邏輯)。并且,在React DOM和React DOM Server上實(shí)現(xiàn)的邏輯也會(huì)有區(qū)別。所以createContext()會(huì)返回一些純粹的對(duì)象(定義如何實(shí)現(xiàn)):
// 一個(gè)簡(jiǎn)單例子 function createContext(defaultValue) { let context = { _currentValue: defaultValue, Provider: null, Consumer: null }; context.Provider = { $$typeof: Symbol.for("react.provider"), _context: context }; context.Consumer = { $$typeof: Symbol.for("react.context"), _context: context, }; return context; }
你會(huì)在某處代碼中使用
所以如果你將react升級(jí)到16.3+,但沒有升級(jí)react-dom,你將使用一個(gè)還不知道Provider和Consumer類型的渲染器,這也就舊版的react-dom可能會(huì)報(bào)錯(cuò):fail saying these types are invalid的原因。
同樣的警告也會(huì)出現(xiàn)在React Native中,但是不同于React DOM,一個(gè)新的React版本不會(huì)立即產(chǎn)生一個(gè)對(duì)應(yīng)的React Native版本。他們(React Native)有自己的發(fā)布時(shí)間表。大概幾周后,渲染器代碼才會(huì)多帶帶更新到React Native庫(kù)中。這就是為什么新特性在React Native生效的時(shí)間會(huì)和React DOM不同。
Okay,那么現(xiàn)在我們知道了react包不包含任何好玩的東西,并且具體的實(shí)現(xiàn)都在像react-dom,react-native這樣的渲染器中。但這并不能回答我們開頭提出的問(wèn)題。React.Component里的setState()是如何和對(duì)應(yīng)的渲染器通信的呢?
答案是每個(gè)渲染器都會(huì)在創(chuàng)建的類中添加一個(gè)特殊的東西,這個(gè)東西叫updater。它不是你添加的東西—恰恰相反,它是React DOM,React DOM Server 或者React Native在創(chuàng)建了一個(gè)類的實(shí)例后添加的:
// React DOM 中是這樣 const inst = new YourComponent(); inst.props = props; inst.updater = ReactDOMUpdater; // React DOM Server 中是這樣 const inst = new YourComponent(); inst.props = props; inst.updater = ReactDOMServerUpdater; // React Native 中是這樣 const inst = new YourComponent(); inst.props = props; inst.updater = ReactNativeUpdater;
從 setState的實(shí)現(xiàn)就可以看出,它做的所有的工作就是把任務(wù)委托給在這個(gè)組件實(shí)例中創(chuàng)建的渲染器:
// 簡(jiǎn)單例子 setState(partialState, callback) { // 使用`updater`去和渲染器通信 this.updater.enqueueSetState(this, partialState, callback); }
React DOM Server 可能想忽略狀態(tài)更新并且警告你,然而React DOM和React Native將會(huì)讓調(diào)節(jié)器的拷貝部分去 處理它。
這就是盡管this.setState()被定義在React包中也可以更新DOM的原因。它調(diào)用被React DOM添加的this.updater并且讓React DOM來(lái)處理更新。
現(xiàn)在我們都比較了解“類”了,但“鉤子”(Hooks)呢?
當(dāng)人們第一次看到 鉤子接口的提案時(shí),他們常回想:useState是怎么知道該做什么呢?這一假設(shè)簡(jiǎn)直比對(duì)this.setState()的疑問(wèn)還要迷人。
但就像我們?nèi)缃窨吹降哪菢樱?b>setState()的實(shí)現(xiàn)一直以來(lái)都是模糊不清的。它除了傳遞調(diào)用給當(dāng)前的渲染器外什么都不做。所以,useState鉤子做的事也是如此。
這次不是updater,鉤子(Hooks)使用一個(gè)叫做“分配器”(dispatcher)的對(duì)象,當(dāng)你調(diào)用React.useState()、React.useEffect()或者其他自帶的鉤子時(shí),這些調(diào)用會(huì)被推送給當(dāng)前的分配器。
// In React (simplified a bit) const React = { // Real property is hidden a bit deeper, see if you can find it! __currentDispatcher: null, useState(initialState) { return React.__currentDispatcher.useState(initialState); }, useEffect(initialState) { return React.__currentDispatcher.useEffect(initialState); }, // ... };
多帶帶的渲染器會(huì)在渲染你的組件之前設(shè)置分配器(dispatcher)。
// In React DOM const prevDispatcher = React.__currentDispatcher; React.__currentDispatcher = ReactDOMDispatcher;let result; try { result = YourComponent(props); } finally { // Restore it back React.__currentDispatcher = prevDispatcher;}
React DOM Server的實(shí)現(xiàn)在這里。由React DOM和React Native共享的調(diào)節(jié)器實(shí)現(xiàn)在這里。
這就是為什么像react-dom這樣的渲染器需要訪問(wèn)和你調(diào)用的鉤子所使用的react一樣的包。否則你的組件將找不到分配器!如果你有多個(gè)React的拷貝在相同的組件樹中,代碼可能不會(huì)正常工作。然而,這總是造成復(fù)雜的Bug,因此鉤子會(huì)在它耗光你的精力前強(qiáng)制你去解決包的副本問(wèn)題。
如果你不覺得這有什么,你可以在工具使用它們前精巧地覆蓋掉原先的分配器(__currentDispatcher的名字其實(shí)我自己編的但你可以在React倉(cāng)庫(kù)中找到它真正的名字)。比如:React DevTools會(huì)使用一個(gè)特殊的內(nèi)建分配器來(lái)通過(guò)捕獲JavaScript調(diào)用棧來(lái)反映(introspect)鉤子。不要在家里重復(fù)這個(gè)(Don’t repeat this at home.)(譯者注:可能是“不要在家里模仿某項(xiàng)實(shí)驗(yàn)”的衍生體??赡苁莻€(gè)笑話,但我get到)
這也意味著鉤子不是React固有的東西。如果在將來(lái)有很多類庫(kù)想要重用相同的基礎(chǔ)鉤子,理論上來(lái)說(shuō)分配器可能會(huì)被移到分離的包中并且被塑造成優(yōu)秀的接口—會(huì)有更少讓人望而生畏的名稱—暴露出來(lái)。在實(shí)際中,我們更偏向去避免過(guò)于倉(cāng)促地將某物抽象,直到我們的確需要這么做。
updater和__currentDispatcher都是泛型程序設(shè)計(jì)(依賴注入/dependency injection)的絕佳實(shí)例。渲染器“注入”特性的實(shí)現(xiàn)。就像setState可以讓你的組件看起來(lái)簡(jiǎn)單明了。
當(dāng)你使用React時(shí),你不需要考慮它是如何工作的。我們期望React用戶去花費(fèi)更多的時(shí)間去考慮它們的應(yīng)用代碼而不是一些抽象的概念比如:依賴注入。但如果你曾好奇this.setState()或useState()是怎么知道它們?cè)撟鍪裁吹模俏蚁M@篇文章將幫助到你。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/100865.html
摘要:譯的生命周期的使用場(chǎng)景原文鏈接作者翻譯上名這個(gè)圖片,就是組件的生命周期,從形成到銷毀的過(guò)程。這并不意味著沒有用。最常見的用例更新以響應(yīng)或更改。是否可以調(diào)用總結(jié)在理想的世界中,我們不會(huì)使用生命周期方法。 [譯]React 的生命周期的使用場(chǎng)景 showImg(https://segmentfault.com/img/bVLTCt?w=2000&h=800); 原文鏈接:React Lif...
摘要:為了使用它們,您可以向組件添加一個(gè)屬性,該屬性的值是一個(gè)回調(diào)函數(shù),它將接收底層的元素或組件的已掛接實(shí)例,作為其第一個(gè)參數(shù)。通常最好使用另一個(gè)生命周期方法,而不是依賴這個(gè)回調(diào)函數(shù),但是很高興知道它存在。 React 常見的面試題 (在 React 里面,你可以知道也可以不知道的事, 但是你會(huì)發(fā)現(xiàn)他們確實(shí)很有用) 根據(jù)記錄,問(wèn)這些問(wèn)題可能不是深入了解他們?cè)谑褂?React 方面的經(jīng)驗(yàn)的最...
摘要:此篇文章我們將會(huì)繼續(xù)探索組件的特性,特別是生命周期。這些方法叫做組件的生命周期方法且會(huì)根據(jù)特定并可預(yù)測(cè)的順序被調(diào)用?;旧纤械慕M件的生命周期方法都可以被分割成四個(gè)階段初始化掛載階段更新階段卸載階段。 原文:https://medium.com/react-ecosystem/react-components-lifecycle-ce09239010df#.j7h6w8ccc 譯者序...
摘要:我現(xiàn)在寫的這些是為了解決和這兩個(gè)狀態(tài)管理庫(kù)之間的困惑。這甚至是危險(xiǎn)的,因?yàn)檫@部分人將無(wú)法體驗(yàn)和這些庫(kù)所要解決的問(wèn)題。這肯定是要第一時(shí)間解決的問(wèn)題。函數(shù)式編程是不斷上升的范式,但對(duì)于大部分開發(fā)者來(lái)說(shuō)是新奇的。規(guī)模持續(xù)增長(zhǎng)的應(yīng) 原文地址:Redux or MobX: An attempt to dissolve the Confusion 原文作者:rwieruch 我在去年大量的使用...
摘要:基礎(chǔ)的理論概念這篇文章是我的一次嘗試,希望能夠形式化的介紹關(guān)于本身的一些理念模型。我對(duì)于此實(shí)際的理念模型是在每次的更新過(guò)程中返回下一個(gè)階段的狀態(tài)。的目標(biāo)是提升對(duì)在動(dòng)畫,布局以及手勢(shì)方面的友好度。我已經(jīng)邀請(qǐng)了團(tuán)隊(duì)的成員來(lái)對(duì)本文檔的準(zhǔn)確性進(jìn)行。 前言 本文主要是對(duì)收集到的一些官方或者其他平臺(tái)的文章進(jìn)行翻譯,中間可能穿插一些個(gè)人的理解,如有錯(cuò)誤疏漏之處,還望批評(píng)指正。筆者并未研究過(guò)源碼,只是...
閱讀 2447·2021-11-19 09:40
閱讀 3617·2021-10-12 10:12
閱讀 1916·2021-09-22 15:04
閱讀 2926·2021-09-02 09:53
閱讀 806·2019-08-29 11:03
閱讀 1153·2019-08-28 18:11
閱讀 1751·2019-08-23 15:28
閱讀 3606·2019-08-23 15:05