這篇文章將為大家詳細(xì)講解有關(guān)React中如何傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、微信小程序開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了調(diào)兵山免費(fèi)建站歡迎大家使用!何時(shí)使用派生狀態(tài)
咱們先來(lái)看一個(gè)比較常見(jiàn)的需求,一個(gè)用戶列表,可以新增和編輯用戶,當(dāng)用戶點(diǎn)擊‘新建'
按鈕用戶可以在輸入框中輸入新的用戶名;當(dāng)點(diǎn)擊‘編輯'按鈕的時(shí)候,輸入框中顯示被編輯的用戶名,用戶可以修改;當(dāng)用戶點(diǎn)擊‘確定'按鈕的時(shí)候用戶列表更新。
class UserInput extends React.Component { state = { user: this.props.user } handleChange = (e) => { this.setState({ user: { ...this.state.user, name: e.target.value } }); } render() { const { onConfirm } = this.props; const { user } = this.state; return (); } } class App extends React.Component { state = { users: [ { id: 0, name: 'bruce' }, { id: 1, name: 'frank' }, { id: 2, name: 'tony' } ], targetUser: {} } onConfirm = (user) => { const { users } = this.state; const target = users.find(u => u.id === user.id); if (target) { this.setState({ users: [ ...users.slice(0, users.indexOf(target)), user, ...users.slice(users.indexOf(target) + 1) ] }); } else { const id = Math.max(...(users.map(u => u.id))) + 1; this.setState({ users: [ ...users, { ...user, id } ] }); } } render() { const { users, targetUser } = this.state; return () } } ReactDOM.render({ users.map(u => (
- {u.name}
)) }, document.getElementById('root'));
運(yùn)行后,效果如圖:
現(xiàn)在點(diǎn)擊‘編輯'和‘新建'按鈕,輸入框中的文字并不會(huì)切換,因?yàn)辄c(diǎn)擊‘編輯'和‘更新'時(shí),雖然UserInput
的props改變了但是并沒(méi)有觸發(fā)state的更新。所以需要實(shí)現(xiàn)props改變引發(fā)state更新,在UserInput
中增加代碼:
componentWillReceiveProps(nextProps) { this.setState({ user: nextProps.user }); }
或者
static getDerivedStateFromProps(props, state) { return { user: props.user }; }
這樣就實(shí)現(xiàn)了UserInput
每次接收新的props的時(shí)候自動(dòng)更新state。但是這種實(shí)現(xiàn)方式是有問(wèn)題的。
派生狀態(tài)導(dǎo)致的問(wèn)題
首先來(lái)明確組件的兩個(gè)概念:受控?cái)?shù)據(jù)(controlled data lives)和不受控?cái)?shù)據(jù)(uncontrollered data lives)。受控?cái)?shù)據(jù)指的是組件中通過(guò)props傳入的數(shù)據(jù),受到父組件的影響;不受控?cái)?shù)據(jù)指的是完全由組件自己管理的狀態(tài),即內(nèi)部狀態(tài)(internal state)。而派生狀態(tài)揉合了兩種數(shù)據(jù)源,當(dāng)兩種數(shù)據(jù)源產(chǎn)生沖突時(shí),問(wèn)題隨之產(chǎn)生。
問(wèn)題一
當(dāng)在修改一個(gè)用戶的時(shí)候,點(diǎn)擊‘確定'按鈕,輸入框里的文字又變成了修改之前的文字。比如我將‘bruce'修改為‘bruce lee',確定后,輸入框中又變成了‘bruce',這是我們不愿意看到的。
出現(xiàn)這個(gè)問(wèn)題的原因是,點(diǎn)擊確定,App會(huì)re-render,App又將之前的user作為props傳遞給了UserInput
。我們當(dāng)然可以在每次點(diǎn)擊確定之后將targetUser
重置為一個(gè)空對(duì)象,但是一旦狀態(tài)多了之后,這樣管理起來(lái)非常吃力。
問(wèn)題二
假設(shè)頁(yè)面加載完成后,會(huì)異步請(qǐng)求一些數(shù)據(jù)然后更新頁(yè)面,如果用戶在請(qǐng)求完成頁(yè)面刷新之前已經(jīng)在輸入框中輸入了一些文字,隨著頁(yè)面的刷新輸入框中的文字會(huì)被清除。
我們可以在App
中加入如下代碼模擬一個(gè)異步請(qǐng)求:
componentDidMount() { setTimeout(() => { this.setState({ text: 'fake request' }) }, 5000); }
導(dǎo)致這個(gè)問(wèn)題的原因在于,當(dāng)異步請(qǐng)求完成,setState
后App
會(huì)re-render,而組件的componentWillReceiveProps
會(huì)在父組件每次render的時(shí)候執(zhí)行,而此時(shí)傳入的user
是一個(gè)空對(duì)象,所以UserInput
的內(nèi)容被清空了。而getDerivedStateFromProps
調(diào)用的更頻繁,會(huì)在組件每次render的時(shí)候調(diào)用,所以也會(huì)產(chǎn)生該問(wèn)題。
為了解決這個(gè)問(wèn)題我們可以在componentWillReceiveProps
中判斷新傳入的user和當(dāng)前的user是否一樣,如果不一樣才設(shè)置state:
componentWillReceiveProps(nextProps) { if (nextProps.user.id !== this.props.user.id) { this.setState({ user: nextProps.user }); } }
更好的解決方案
派生狀態(tài)的數(shù)據(jù)源的不確定性會(huì)導(dǎo)致各種問(wèn)題,那如果每份數(shù)據(jù)有且只被一個(gè)component管理應(yīng)該就能避免這些問(wèn)題了。這種思路有兩種實(shí)現(xiàn),一種是數(shù)據(jù)完全由父組件管理,一種是數(shù)據(jù)完全由組件自己管理。下面分別討論:
完全受控組件(fully controlled component)
組件的數(shù)據(jù)完全來(lái)自于父組件,組件自己將不需要管理state。我們新建一個(gè)完全受控版的UserInput
:
class FullyControlledUserInput extends React.Component { render() { const { user, onConfirm, onChange } = this.props; return () } }
App中調(diào)用FullyControlledUserInput
的方法如下:
...{ this.setState({ targetUser: { id: targetUser.id, name: e.target.value } }); }} onConfirm={this.onConfirm} /> ...
現(xiàn)在FullyControlledUserInput
中的所有的數(shù)據(jù)都來(lái)源于父組件,由此解決數(shù)據(jù)沖突和被篡改的問(wèn)題。
完全不受控組件(fully uncontrolled component)
組件的數(shù)據(jù)完全由自己管理,因此componentWillReceiveProps
中的代碼都可以移除,但保留傳入props來(lái)設(shè)置state初始值:
class FullyUncontrolledUserInput extends React.Component { state = { user: this.props.user } onChange = (e) => { this.setState({ user: { ...this.state.user, name: e.target.value } }); } render() { const { user } = this.state; const { onConfirm } = this.props; return () } }
當(dāng)傳入的props發(fā)生改變時(shí),我們可以通過(guò)傳入一個(gè)不一樣的key
來(lái)重新創(chuàng)建一個(gè)component的實(shí)例來(lái)實(shí)現(xiàn)頁(yè)面的更新。App中調(diào)用FullyUncontrolledUserInput
的方法如下::
大部分情況下,這是更好的解決方案?;蛟S有人會(huì)覺(jué)得這樣性能會(huì)受影響,其實(shí)性能并不會(huì)變慢多少,而且如果組件的更新邏輯過(guò)于復(fù)雜的話,還不如重新創(chuàng)建一個(gè)新的組件來(lái)的快。
在父組件中調(diào)用子組件的方法設(shè)置state
如果某些情況下沒(méi)有合適的屬性作為key
,那么可以傳入一個(gè)隨機(jī)數(shù)或者自增的數(shù)字作為key,或者我們可以在組件中定義一個(gè)設(shè)置state的方法并通過(guò)ref
暴露給父組件使用,比如我們可以在UserInput
中添加:
setNewUserState = (newUser) => { this.setState({ user: newUser }); }
在App中通過(guò)ref調(diào)用這個(gè)方法:
...
這個(gè)方法不推薦使用,除非實(shí)在沒(méi)法了。。
關(guān)于“React中如何傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。