在本文中,我們將把目光聚焦于 React 組件內(nèi)部的狀態(tài)管理,去認(rèn)識(shí)或重新思考以下三個(gè)核心概念:
props
和 state
函數(shù)組件
類(lèi)組件
讓我們開(kāi)始吧!
01. React 中的數(shù)據(jù)
站在“組件”的角度上,React 把應(yīng)用中流動(dòng)的數(shù)據(jù)分為兩種類(lèi)型:
不可更改內(nèi)容,但可以單向跨組件傳遞的 props;
可以更改內(nèi)容,但不能跨組件傳遞的 state;
進(jìn)一步說(shuō),props 和 state 的區(qū)別在于,props 是外部的,并且被任何渲染這個(gè)組件的代碼所控制。而 state 則是內(nèi)部的,并且被組件自身所控制。
非計(jì)算機(jī)專(zhuān)業(yè)的初學(xué)者經(jīng)常困惑 props 和 state 在名稱(chēng)與含義上的關(guān)聯(lián),其實(shí)大可不必在意,他們本質(zhì)上只是數(shù)據(jù)的別稱(chēng),只是在 React 中,它們被各自賦予了特殊的限制或能力。
你可以通過(guò)組件上的 props 屬性,像在 HTML 中傳遞屬性一樣,將你想要傳遞的任何數(shù)據(jù)傳遞給子組件,所有的屬性都會(huì)被存儲(chǔ)在子組件(類(lèi)組件)的 this.props 對(duì)象中。
function Parent(props) { return <Child name={"Tom"} /> }function Child(props) { return <span>(props.name)</span> }
02. 函數(shù)組件
我們之前提到過(guò),React 使用組件渲染視圖提升性能,而組件即是一個(gè)函數(shù),可以用一個(gè)公式來(lái)簡(jiǎn)潔的表示其功用:f(數(shù)據(jù)) => UI。到這里我想你應(yīng)該注意到了,為什么我們說(shuō) React 并不是一個(gè)大型的 MVC (或 MVVM)框架,因?yàn)?React 只負(fù)責(zé)視圖層(View)的渲染,其他的事情將由 React 生態(tài)中的其他工具來(lái)完成。
話(huà)說(shuō)回來(lái),對(duì)于 React 組件而言,最簡(jiǎn)單的一種形式莫過(guò)于函數(shù)組件了,它充分展現(xiàn)了 React 的哲學(xué),一次只做一件事,組件化和數(shù)據(jù)驅(qū)動(dòng)UI。
函數(shù)組件又稱(chēng)為“無(wú)狀態(tài)組件”,“受控組件”或“木偶組件”,因?yàn)楹瘮?shù)組件只負(fù)責(zé)接收 props 并返回 UI,它自身并不能擁有可改變的數(shù)據(jù),在真實(shí)的 React 應(yīng)用開(kāi)發(fā)場(chǎng)景下,我們經(jīng)常盡可能的使用函數(shù)組件,將整個(gè)應(yīng)用的 UI 拆分成盡可能小的視覺(jué)單元。
這是因?yàn)楹瘮?shù)組件是非常直觀的,它接收屬性返回元素,內(nèi)部邏輯清晰明確,而且更重要的是,函數(shù)組件內(nèi)沒(méi)有 this 關(guān)鍵字,因此你永遠(yuǎn)不用擔(dān)心煩人的“this上下文問(wèn)題”。
記?。喝绻愕慕M件不需要追蹤內(nèi)部狀態(tài),盡量使用函數(shù)組件。
03. 類(lèi)組件
和函數(shù)組件相對(duì)應(yīng)的,便是“類(lèi)組件”了,類(lèi)似的,它也被稱(chēng)為“有狀態(tài)組件”,“非受控組件”和“容器組件”。這里需要注意,雖然我們按照代碼的形式為兩種類(lèi)型的組件命名,但這并不嚴(yán)謹(jǐn),因?yàn)樵?JavaScript 中,“類(lèi)”也是函數(shù)。
不同于函數(shù)組件,類(lèi)組件擁有著可以更改的內(nèi)部數(shù)據(jù) -- state。它最終影響著頁(yè)面的渲染情況,而且 state 可以被組件在任何時(shí)刻在內(nèi)部修改。通常的時(shí)刻時(shí)用戶(hù)與界面發(fā)生交互的時(shí)候。
由于 React 把變化的數(shù)據(jù)封裝在組件內(nèi)部,并堅(jiān)持單向數(shù)據(jù)流的原則。我們有了高度抽象的 UI 組件,并封裝復(fù)雜的業(yè)務(wù)邏輯。這使得我們可以通過(guò)構(gòu)建,組合一系列小組件開(kāi)發(fā)出大型應(yīng)用。
那么應(yīng)該如何向類(lèi)組件添加 state 呢?很簡(jiǎn)單,我們所要做的只是在類(lèi)組件內(nèi)部添加一個(gè) state 屬性,state 屬性是一個(gè)對(duì)象。這個(gè)對(duì)象代表了組件的狀態(tài),對(duì)象的每一個(gè)屬性名都代表組件的一個(gè)特定的狀態(tài),下面是具體的代碼:
import React from "react"class Parent extends React.Component { state = { name: "Eliot", } render() { return <p>{this.state.name}</p> } }
React 使我們迫使大腦關(guān)注兩個(gè)重要的部分:
組件看起來(lái)是什么樣?
組件當(dāng)前的狀態(tài)是什么?
通過(guò)讓組件管理自己的狀態(tài),任何時(shí)候狀態(tài)的變更都會(huì)令 React 自動(dòng)更新相應(yīng)的頁(yè)面部分。這便是使用 React 構(gòu)建組件的主要優(yōu)勢(shì)之一:當(dāng)頁(yè)面需要重新渲染時(shí),我們僅僅需要思考的是如何更改狀態(tài)。我們不必跟蹤頁(yè)面的哪些部分需要更改,不需要決定如何有效的重新呈現(xiàn)頁(yè)面,React 自會(huì)比較先前的輸出和新的輸出,決定什么應(yīng)該發(fā)生改變,并為我們做出決定。而這個(gè)確定之前改變了什么和現(xiàn)在應(yīng)該新輸出什么的過(guò)程有一個(gè)專(zhuān)門(mén)的名詞,叫做 Reconciliation。
04. 修改 state
你應(yīng)該還記得類(lèi)組件與函數(shù)組件最大的不同在于類(lèi)組件自身?yè)碛锌梢愿淖儍?nèi)部數(shù)據(jù)的能力。那么如何行使這一能力呢?和直覺(jué)不同,要做到這一點(diǎn),你需要使用 React 提供的專(zhuān)門(mén)的 API:this.setState()。
你有兩種方式使用該 API:
設(shè)置對(duì)象參數(shù);
設(shè)置函數(shù)參數(shù);
讓我們先來(lái)看看第一種:
this.setState({ name: "Tom"})
React 會(huì)自動(dòng)合并對(duì) state 的改變。而有時(shí),你的組件需要一個(gè)新的 state ,而這個(gè) state 的變化又依賴(lài)于舊的 state值,每當(dāng)這種時(shí)候,你就該使用第二種 API 調(diào)用方式:
this.setState((prevState) => ({ name: "mr." + prevState.name }))
講到這里你可能會(huì)感到奇怪,只是更新 state 而已,為什么還需要調(diào)用一個(gè)專(zhuān)門(mén)的 API?我們直接修改之前定義的 state對(duì)象不就好了嗎?之所以這樣設(shè)計(jì)的原因是,組件內(nèi) state 的變化不僅僅是對(duì)象屬性值發(fā)生變化那么簡(jiǎn)單,它還需要驅(qū)動(dòng)整個(gè) UI 進(jìn)行重新渲染,因此 this.setState() 這個(gè) API 被調(diào)用時(shí)實(shí)際上做了兩件事:
修改 state 對(duì)象;
驅(qū)動(dòng)組件重新渲染;
如果你對(duì) React 有一定研究,你可能會(huì)質(zhì)疑我以上所羅列的兩點(diǎn)并不精確,的確如此,小小的 this.setState() API 其實(shí)內(nèi)部還有很多細(xì)節(jié)值得注意,例如,當(dāng)調(diào)用 this.setState() 時(shí)并不會(huì)立即改變 state 的值,也當(dāng)然不會(huì)立即重新渲染組件。例如,當(dāng)以對(duì)象為參數(shù)調(diào)用 this.setState() API 時(shí),盡管內(nèi)部重復(fù)為數(shù)據(jù)賦值,最終的數(shù)據(jù)也只保留最后一次更改的結(jié)果。
不過(guò)幸好,這些略顯古怪的狀態(tài)早有前人為我們做了詳盡的解釋?zhuān)绻愀信d趣,請(qǐng)點(diǎn)擊下方鏈接查詢(xún)更多的信息:
setState:這個(gè)API設(shè)計(jì)到底怎么樣
問(wèn)一個(gè)react更新State的問(wèn)題?
05. 控制組件
當(dāng)你在 Web 應(yīng)用中使用表單時(shí),這個(gè)表單的數(shù)據(jù)被存儲(chǔ)于相應(yīng)的 DOM 節(jié)點(diǎn)內(nèi)部,但正如我們之前提到的,React 的整個(gè)關(guān)鍵點(diǎn)就在于如何高效的管理應(yīng)用內(nèi)的狀態(tài)。所以雖然表單的數(shù)據(jù)被存儲(chǔ)于 DOM 中,React 依然可以對(duì)它進(jìn)行狀態(tài)管理。
而管理的方式即是使用“控制組件”。簡(jiǎn)單而言,“控制組件”會(huì)渲染出一個(gè)表單,但是將表單所需的所有真實(shí)數(shù)據(jù)作為 state 存儲(chǔ)于組件內(nèi)部,而不是 DOM 中。
之所以被稱(chēng)為“控制組件”的原因也即在于此,“控制組件”控制著組件內(nèi)的表單數(shù)據(jù),因此,唯一更新表單數(shù)據(jù)的方式就是更新組件內(nèi)部對(duì)應(yīng)的 state 值。
import React as "react"class Input extends React.Component { state = { value: "enter something...", } handleClick: (e) => { this.setState({value: e.target.value}) } render() { <input value={this.state.value} onKeyup={this.handleClick} /> } }
可以看到,我們使用 handleClick 方法響應(yīng)用戶(hù)每一次鍵盤(pán)敲擊以即時(shí)更新表單狀態(tài),這樣做不僅天然的支持了即時(shí)的輸入驗(yàn)證,還允許你有條件的禁止或點(diǎn)亮表單按鈕。
06. 小結(jié)
這篇文章我們介紹了 React 的兩種數(shù)據(jù)形式:state 和 props,并且介紹了 React 組件的兩種形式:函數(shù)組件與類(lèi)組件,希望各位有所收獲。
相關(guān)推薦:
react的使用: React如何渲染UI
React的使用:react框架的五大特點(diǎn)
聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com