摘要:首先賣個(gè)關(guān)子,下面我們一起來復(fù)習(xí)下小學(xué)還是初中的一枚數(shù)學(xué)知識(shí)。一旦更改了,會(huì)觸發(fā)組件的重新渲染。為了頁面渲染性能的考慮,有助于在中進(jìn)行比較并確定是否重新渲染。
概念引入
對(duì)于React來說, 沒有State就沒有頁面的渲染, 我們也將什么都看不到
咋一聽怎么那么唬人?不過的確是這樣,正如標(biāo)題所言State是UI的靈魂。我們都知道React的核心思想之一是組件化,將頁面所展示的東西按一定的規(guī)則分割成很多份并進(jìn)行一一封裝最后抽象成我們現(xiàn)在所稱為的"組件", 就好像我們搭積木一樣,一個(gè)城堡就是通過一個(gè)個(gè)小方塊堆疊在一起的。但是FaceBook覺得如果僅僅是簡單的封裝那么和普通的moudle有什么區(qū)別?和咸魚又有什么區(qū)別?于是FaceBook給這些"組件"賦予了靈魂(之一) -- State。
什么叫State?顧名思義就是狀態(tài)的意思。每個(gè)組件都有自己的狀態(tài),比如開關(guān)的閉合、顏色的切換和顯示與隱藏等等都會(huì)涉及到狀態(tài)的切換。首先賣個(gè)關(guān)子,下面我們一起來復(fù)習(xí)下小學(xué)(還是初中?)的一枚數(shù)學(xué)知識(shí)。
y=f(x) -->(假如這是一個(gè)一元一次函數(shù))
Are you kidding me?這是要侮辱在座的智商?不不不,請(qǐng)放下手里40米的大刀,筆者想拋個(gè)磚引個(gè)玉。
這是再簡單不過的了,它表示y是關(guān)于x的函數(shù)。函數(shù)在數(shù)學(xué)中是十分的嚴(yán)謹(jǐn),x與y是一一對(duì)應(yīng)關(guān)系換句話說就是同一個(gè)x代入運(yùn)算得到的永遠(yuǎn)是同一個(gè)y;同一個(gè)y代入運(yùn)算得到的永遠(yuǎn)是同一個(gè)x,這個(gè)特性很像Redux中的Reducer,本質(zhì)上是一個(gè)純函數(shù)。
那么如果我們把這個(gè)公式帶到React中會(huì)有什么樣的化學(xué)反應(yīng)呢?
UI=f(State)
有木有感覺眼前一亮,Excuse me?竟然把State和UI通過一個(gè)公式關(guān)聯(lián)起來?其實(shí)本質(zhì)上就是這么簡單。
同時(shí),我們還可以繼續(xù)用函數(shù)的思想去思考它:
輸入特定的State只能輸出特定的UI
根據(jù)特定的UI就能反推出相應(yīng)的State
當(dāng)然實(shí)際結(jié)果也是如此,React組件所渲染出來的東西與State有直接而且唯一的關(guān)系,換句話說就是State決定組件顯示什么而且只有State才能決定組件顯示什么。
比如上面提到的一個(gè)組件可能有很多切換的動(dòng)作,開關(guān)、顏色、顯示與消失等等...本來這種切換的動(dòng)作需要我們自己通過操作DOM來實(shí)現(xiàn),但是FaceBook在設(shè)計(jì)React之初就把 直接操作DOM 這條路給堵死了(但仍給我們提供的必要的接口已備不時(shí)之需,后續(xù)文章會(huì)有相應(yīng)內(nèi)容),究竟為什么要這么做?因?yàn)槲覀兌贾狼岸碎_發(fā)中特別消耗性能的是DOM操作,一旦處理不當(dāng)就會(huì)影響整個(gè)頁面的展示效果,因此FaceBook一不做二不休直接搞出了一個(gè)State出來,讓開發(fā)者去輸入State隨后React自己去操作相應(yīng)的DOM。
這么做有兩個(gè)好處
使得State成為頁面的唯一數(shù)據(jù)來源和頁面元素變換的唯一依據(jù)。
提高頁面的渲染性能(當(dāng)然這不是React高效渲染的決定性因素)。
State大大提高了開發(fā)者對(duì)React組件的開發(fā)效率而不用擔(dān)心頁面性能問題,可謂是一舉多得。
實(shí)例展示:靜態(tài)State下面我們來開發(fā)一個(gè)簡單的文字展示組件:
import React, {Component} from "react" ---line 1 class Show extends Component { ---line 2 constructor(props) { ---line 3 super(props); this.state = { ---line 4 content: "Hello World" } //this.propName = propValue; ---line 5 } render() { ---line 6 return ({this.state.content}
---line 7 ) } } export default Show;
首先一起來分析下這段代碼:
line 1: 日常導(dǎo)包
line 2: ES6創(chuàng)建對(duì)象的方法。強(qiáng)烈推薦這么寫
line 3: 該組件的構(gòu)造方法,如果組件有屬性默認(rèn)值那么就需要寫構(gòu)造函數(shù)
line 4: 這里表示該組件有自己的State屬性而且它還是一個(gè)字面量對(duì)象,所以與該組件有關(guān)的所有State都應(yīng)該寫在這個(gè)字面量對(duì)象中。從代碼中看出該組件有一個(gè)State對(duì)象content,它包含著這個(gè)組件需要展示的一段文字。
line 5: 如果我們想給這個(gè)組件定義State以外的屬性,那么就可以項(xiàng)這行所寫一樣,不過需要放在this對(duì)象中,這樣才能在組件中通過this對(duì)象讀取到。
line 6: render方法是最終構(gòu)建組件結(jié)構(gòu)的地方,因?yàn)榻M件究竟長什么樣子,需要在這里寫。
line 7: 正如這個(gè)組件需要做的事情,我們?cè)趓ender方法中返回這個(gè)p標(biāo)簽用來顯示文字信息。因?yàn)樗枰奈淖中畔⒈4嬖赟tate對(duì)象中,State又保存在this對(duì)象中,所以如何去獲取文字信息在這里不需要過多贅述。另外需要注意的是,render方法return的節(jié)點(diǎn)只能是一個(gè),不能是多個(gè)。如果你的組件結(jié)構(gòu)復(fù)雜,請(qǐng)?jiān)谧钔鈱佑胐iv這樣的標(biāo)簽包一下然后再返回。
下面看具體效果:
是不是很簡單?
接下來再說一下狀態(tài)變化,因?yàn)闋顟B(tài)就是用來更改的,如果不更改那和咸魚有什么區(qū)別?
先思考下:React狀態(tài)應(yīng)該如何更改?
這個(gè)問題筆者第一次遇到的時(shí)候第一反應(yīng)就是:直接改?。。?!(然后被piapiapia打臉),先試下吧:
import React, {Component} from "react" class Show extends Component { constructor(props) { super(props); this.state = { content: "Hello World" } } changeState = () => { this.state.content = "I"m React State"; }; render() { return () } } export default Show;{this.state.content}
我們創(chuàng)建一個(gè)函數(shù)用來更改響應(yīng)的State,然后實(shí)際運(yùn)行的時(shí)候發(fā)現(xiàn)不管怎么點(diǎn)按鈕都毫無作用?Why?
因?yàn)镽eact給我們提供了專門用于更改狀態(tài)的方法, this.setState()
我們來重新試一下:
import React, {Component} from "react" class Show extends Component { constructor(props) { super(props); this.state = { content: "Hello World" } } changeState = () => { this.setState({ content: "I"m React State" }) }; render() { return () } } export default Show;{this.state.content}
這個(gè)方法需要我們傳入一個(gè)字符量對(duì)象,key是我們需要更改的那個(gè)State,這里是content; value是我們所期望要更改的值。( [] 是轉(zhuǎn)移符)。
可以看出頁面中那行字變成了我們所期望的文字。所以正如我們所說:
異步的setState更改State要使用this.setState()方法。
一旦更改了State,會(huì)觸發(fā)組件的重新渲染。實(shí)際上是運(yùn)行一次組件中的render方法
這個(gè)點(diǎn)筆者想不出合適的引入點(diǎn),所以就直接拋出來了。這個(gè)問題很有趣,因?yàn)榍珊系氖枪P者的一個(gè)萌妹子同事在學(xué)習(xí)React時(shí)候恰好遇到這個(gè)問題,代碼大概是這樣:
onchange = () => { this.setState({ name: "Demo" }); this.props.change(this.state.name)//調(diào)用父組件通過props傳過來的方法 };
她的本意是在本組件更改了name這個(gè)State后再通過調(diào)用父組件的方法實(shí)現(xiàn)父組件name的重新渲染。我們看出this.props.change(this.state.name)傳入的參數(shù)是直接從State中取的,但是實(shí)際運(yùn)行的時(shí)候卻不是如想象中那樣同時(shí)更改,現(xiàn)象是第一次點(diǎn)擊時(shí)候本組件成功渲染,但是父組件并沒有同時(shí)渲染;第二次點(diǎn)擊時(shí)候父組件才渲染成對(duì)應(yīng)的名字。
為什么呢?
因?yàn)閠his.setState這個(gè)方法不是同步的而是異步的,了解JavaScript中Event Loop機(jī)制的朋友都知道,如果一段js代碼中有異步的代碼那么會(huì)將其放在一個(gè)隊(duì)列中,等待這段代碼其余代碼運(yùn)行完后再從那個(gè)隊(duì)列中取出異步代碼運(yùn)行。this.setState機(jī)制也和它差不多,當(dāng)我們set一個(gè)State后React并不會(huì)立即去更改對(duì)應(yīng)的State,而是在合適的時(shí)機(jī)下進(jìn)行更改甚至為了提高性能會(huì)將多個(gè)setState過程合并成一個(gè)。為了證明這個(gè)異步機(jī)制,我們通過打印的方式做個(gè)試驗(yàn):
import React, {Component} from "react" class Show extends Component { constructor(props) { super(props); this.state = { content: "Hello World" } } changeState = () => { console.log(`1 -- ${this.state.content}`); this.setState({ content: "I"m React State" }); console.log(`2 -- ${this.state.content}`); console.log("end"); }; render() { return () } } export default Show;{this.state.content}
控制臺(tái)打印結(jié)果如下:
可以看出,頁面正常渲染了說明對(duì)應(yīng)的State已經(jīng)更改了,但是控制臺(tái)顯示的信息卻沒有更改后的現(xiàn)象,所以可以確定真正的setState并不是調(diào)用this.setState()方法的瞬間,而是在之后的某個(gè)時(shí)間。所以有個(gè)問題需要注意:不要用當(dāng)前的State去計(jì)算下一個(gè)State,因?yàn)槟悴荒鼙WC當(dāng)前的State是最新的
但如果有個(gè)需求,需要在更改State后立即執(zhí)行某個(gè)動(dòng)作怎么辦?
正常來說我們無法預(yù)知真正的setState是在何時(shí),所以React理所當(dāng)然得給我們提供了辦法,那么就是this.setState的第二個(gè)參數(shù),第二個(gè)參數(shù)是一個(gè)方法,當(dāng)對(duì)應(yīng)的State修改成功后會(huì)立即執(zhí)行,我們修改下代碼:
... changeState = () => { console.log(`1 -- ${this.state.content}`); this.setState({ content: "I"m React State" }, () => { console.log(`3 -- ${this.state.content}`); }); console.log(`2 -- ${this.state.content}`); console.log("end"); }; ...
看結(jié)果咯:
與預(yù)期一致,沒毛?。。?!
State的不可變看到這個(gè)小標(biāo)題,估計(jì)很多人會(huì)很懵逼,前面還說不更改的State和咸魚有什么區(qū)別怎么到這里就要不可變了?其實(shí)是混淆了。
官方的建議是將State的所有對(duì)象當(dāng)做是不可變對(duì)象,一旦每個(gè)對(duì)象更改了那么需要重新創(chuàng)建這個(gè)對(duì)象。舉例子說,前面的代碼中有:
this.state = { content: "Hello World" }
當(dāng)我們更改了content的值,用"I"m React State"替換了原有的"Hello World"。其實(shí)在這里,content對(duì)用的value不僅僅是內(nèi)容上的變化也是地址上的變化,這種在基本變量上體現(xiàn)不出來,比如我們有個(gè)State要保存一個(gè)列表內(nèi)容那么就得是個(gè)數(shù)組(字面量對(duì)象亦如此):
this.state = { navis: ["React","Vue","Angular"] }
這個(gè)時(shí)候如果我們直接將navis的值拿出來push一個(gè)元素進(jìn)去然后setState:
addNavi = () => { this.setState({ navis: this.state.navis.push("React-Native") }) };
結(jié)果是頁面并沒有重新渲染,Why? 因?yàn)镽eact在對(duì)比navis新的和老的兩個(gè)值時(shí)候發(fā)現(xiàn)它們的地址都沒變化就認(rèn)為它們內(nèi)容也沒變化就不會(huì)重新渲染。這是個(gè)坑?。?!。所以此時(shí)State對(duì)象的不可變?cè)瓌t就有作用了,解決方案有兩個(gè):
1、 復(fù)制原來的值,push完后進(jìn)行setState。
addNavi = () => { let navisCopy = this.state.navis.slice(); this.setState({ navis: navisCopy.push("React-Native") }) };
這樣就能正常運(yùn)行了,因?yàn)?b>navis對(duì)應(yīng)的值不僅僅在內(nèi)容上變了,地址也變化了,React檢測到變化后就進(jìn)行了重新渲染。
2、第三方插件
Immutable.js
immutability-helper
immutability-helper-x
至于為什么需要這么做?
State數(shù)據(jù)更明確,方便管理和開發(fā)調(diào)試。
為了頁面渲染性能的考慮,有助于在shouldComponentUpdate中進(jìn)行比較并確定是否重新渲染。
Bingo...本期的博文就結(jié)束了,這期筆者也精心準(zhǔn)備了很久,希望大家都能喜歡!!
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/97365.html
摘要:所以還是印證那句話是組件渲染的唯一依據(jù)。所以對(duì)組件的進(jìn)行約束是創(chuàng)建一個(gè)健康組件的必要條件。這里我們約束屬性類型為。使用方式運(yùn)行結(jié)果沒有錯(cuò)誤假如我們?cè)偌尤胍粋€(gè)子組件控制臺(tái)如預(yù)期報(bào)錯(cuò)自定義約束萬物皆有其局限性。 日常扯淡前的廢話 上一篇我們介紹了React中State對(duì)象,說到它是組件渲染的唯一依據(jù);當(dāng)然我們也可以認(rèn)為State是組件中的數(shù)據(jù)源之一,它保存著組件渲染的所有數(shù)據(jù)并且可以直接作...
摘要:所謂的時(shí)間旅行從廣義上來說無非就是三個(gè)動(dòng)作回到過去進(jìn)入未來回到現(xiàn)在,這個(gè)無論是從現(xiàn)實(shí)還是前端技術(shù)來說都是可靠的。單從技術(shù)棧來說,時(shí)間旅行不是一門技術(shù)而是一個(gè)思想套路。 標(biāo)題看起來挺新穎的,筆者都覺得很高大上是不是哈哈... 拋轉(zhuǎn) 時(shí)間旅行在生活中是一個(gè)非常吸引人的概念,雖然現(xiàn)在無法實(shí)現(xiàn)但說不定未來的某天就實(shí)現(xiàn)了!然后就穿梭會(huì)過去殺掉小時(shí)候的自己然后就開始懵逼自己是誰類似的狗血?jiǎng)∏?.....
摘要:官方對(duì)的介紹是意思就是提供了一種通過組件樹傳遞數(shù)據(jù)的方法,而無需在每個(gè)級(jí)別手動(dòng)傳遞。這也是基于重要物證哈哈實(shí)例使用學(xué)習(xí)技術(shù)最終是要有產(chǎn)出的。依然被視作一個(gè)組件,不過不同的是它的子組件必須是一個(gè)方法并且該方法接收當(dāng)前對(duì)象并最終返回一個(gè)節(jié)點(diǎn)。 拋轉(zhuǎn)引玉 通過上一篇的科普我們知道如果父節(jié)點(diǎn)需要向子節(jié)點(diǎn)傳遞數(shù)據(jù),那么就得通過Props來實(shí)現(xiàn);那么擺在我們眼前的就有一個(gè)問題了:現(xiàn)有N個(gè)節(jié)點(diǎn)并且它...
摘要:但這樣做的缺點(diǎn)很多,不利于狀態(tài)在組件之間共享。所以本篇使用作為狀態(tài)管理器來實(shí)現(xiàn)時(shí)間旅行。并且從中可以看出過程不僅僅向?qū)ο笾刑砑右粋€(gè)狀態(tài)對(duì)象,還對(duì)進(jìn)行了加一操作,這是為了保證狀態(tài)與保持同步。 距離上一次更新已經(jīng)有半個(gè)月了,這半個(gè)月來主要在忙兩件事:一個(gè)是最近老板給了個(gè)自動(dòng)化測試任務(wù),另一個(gè)是和學(xué)校的弟弟一起搞一個(gè)微信小游戲...emmmm!其實(shí)主要是懶!??! 本篇是作為上篇的續(xù)集,不知道...
摘要:語法將語法直接加入到代碼中,再通過翻譯器裝換到純后由瀏覽器執(zhí)行。事實(shí)上,并不需要花精力學(xué)習(xí)。可以說,基本語法基本被囊括了,但也有少許不同。明確的數(shù)據(jù)流動(dòng)。這條原則讓組件之間的關(guān)系變得簡單且可預(yù)測。使用獲取和顯示回調(diào)。 JSX語法 JSX將HTML語法直接加入到JavaScript代碼中,再通過翻譯器裝換到純JavaScript后由瀏覽器執(zhí)行。在實(shí)際開發(fā)中,JSX在產(chǎn)品打包階段都已經(jīng)編...
閱讀 1547·2021-11-24 10:17
閱讀 1043·2021-09-29 09:43
閱讀 2170·2021-09-23 11:21
閱讀 2187·2019-08-30 14:13
閱讀 1305·2019-08-29 13:58
閱讀 3166·2019-08-28 17:51
閱讀 1821·2019-08-26 13:29
閱讀 2986·2019-08-26 10:13