最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解React中傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法

 更新時(shí)間:2018年09月13日 09:29:22   作者:MudOnTire  
這篇文章主要介紹了詳解React中傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

我們使用react的時(shí)候常常需要在一個(gè)組件傳入的props更新時(shí)重新渲染該組件,常用的方法是在componentWillReceiveProps中將新的props更新到組件的state中(這種state被成為派生狀態(tài)(Derived State)),從而實(shí)現(xiàn)重新渲染。React 16.3中還引入了一個(gè)新的鉤子函數(shù)getDerivedStateFromProps來(lái)專門(mén)實(shí)現(xiàn)這一需求。但無(wú)論是用componentWillReceiveProps還是getDerivedStateFromProps都不是那么優(yōu)雅,而且容易出錯(cuò)。所以今天來(lái)探討一下這類(lèi)實(shí)現(xiàn)會(huì)產(chǎn)生的問(wèn)題和更好的實(shí)現(xiàn)方案。

何時(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 (
   <div>
    <input value={user.name || ''} onChange={this.handleChange} />
    <button onClick={() => { onConfirm(user) }}>確定</button>
   </div>
  );
 }
}

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 (
   <div>
    <UserInput user={targetUser} onConfirm={this.onConfirm} />
    <ul>
     {
      users.map(u => (
       <li key={u.id}>
        {u.name}
        <button onClick={() => { this.setState({ targetUser: u }) }}>編輯</button>
       </li>
      ))
     }
    </ul>
    <button onClick={() => { this.setState({ targetUser: {} }) }}>新建</button>
   </div>
  )
 }
}

ReactDOM.render(<App />, 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)求完成,setStateApp會(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 (
   <div>
    <input value={user.name || ''} onChange={onChange} />
    <button onClick={() => { onConfirm(user) }}>確定</button>
   </div>
  )
 }
}

App中調(diào)用FullyControlledUserInput的方法如下:

...
  <FullyControlledUserInput
   user={targetUser}
   onChange={(e) => {
    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 (
   <div>
    <input value={user.name || ''} onChange={this.onChange} />
    <button onClick={() => { onConfirm(user) }}>確定</button>
   </div>
  )
 }
}

當(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的方法如下::

<FullyUncontrolledUserInput
 user={targetUser}
 onConfirm={this.onConfirm}
 key={targetUser.id}
/>

大部分情況下,這是更好的解決方案?;蛟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è)方法:

  ...
  
  <UserInput user={targetUser} onConfirm={this.onConfirm} ref='userInput' />
   <ul>
   {
    users.map(u => (
     <li key={u.id}>
      {u.name}
      <button onClick={() => {
       this.setState({ targetUser: u });
       this.refs.userInput.setNewUserState(u);
      }}>
       編輯
      </button>
     </li>
    ))
   }
  </ul>
  <button onClick={() => {
   this.setState({ targetUser: {} });
   this.refs.userInput.setNewUserState({});
  }}>
   新建
  </button>
  
  ...

這個(gè)方法不推薦使用,除非實(shí)在沒(méi)法了。。

本文源碼請(qǐng)參考:ways-to-update-component-on-props-change

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • React用法之高階組件的用法詳解

    React用法之高階組件的用法詳解

    高階組件也就是我們常說(shuō)的HOC,是React中用于復(fù)用組件邏輯的一種高級(jí)技巧。這篇文章主要通過(guò)一些示例帶大家學(xué)習(xí)一下高階組件的使用,希望對(duì)大家有所幫助
    2023-04-04
  • React Router v4 入坑指南(小結(jié))

    React Router v4 入坑指南(小結(jié))

    這篇文章主要介紹了React Router v4 入坑指南(小結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-04-04
  • React處理復(fù)雜圖片樣式的方法詳解

    React處理復(fù)雜圖片樣式的方法詳解

    這篇文章主要為大家詳細(xì)介紹了React處理復(fù)雜圖片樣式的方法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-04-04
  • React?Refs?的使用forwardRef?源碼示例解析

    React?Refs?的使用forwardRef?源碼示例解析

    這篇文章主要為大家介紹了React?之?Refs?的使用和?forwardRef?的源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 使用webpack搭建react開(kāi)發(fā)環(huán)境的方法

    使用webpack搭建react開(kāi)發(fā)環(huán)境的方法

    本篇文章主要介紹了使用webpack搭建react開(kāi)發(fā)環(huán)境的方法,在這篇文章中我們開(kāi)始利用我們之前所學(xué)搭建一個(gè)簡(jiǎn)易的React開(kāi)發(fā)環(huán)境,用以鞏固我們之前學(xué)習(xí)的Webpack知識(shí)。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • 基于React編寫(xiě)一個(gè)全局Toast的示例代碼

    基于React編寫(xiě)一個(gè)全局Toast的示例代碼

    前些日子在做項(xiàng)目的時(shí)候,需要封裝一個(gè)Toast組件,我想起之前用過(guò)的庫(kù),只要在入口文件中引入就可以在全局中使用,還是很方便的,借這次機(jī)會(huì)也來(lái)實(shí)現(xiàn)一下,所以本文介紹了React中如何編寫(xiě)一個(gè)全局Toast,需要的朋友可以參考下
    2024-05-05
  • JS中使用react-tooltip插件實(shí)現(xiàn)鼠標(biāo)懸浮顯示框

    JS中使用react-tooltip插件實(shí)現(xiàn)鼠標(biāo)懸浮顯示框

    前段時(shí)間遇到的一個(gè)需求,要求鼠標(biāo)懸停顯示使用描述, 用到了react-tooltip插件,今天寫(xiě)一個(gè)總結(jié),感興趣的朋友跟隨小編一起看看吧
    2019-05-05
  • React、Vue中key的作用詳解 (key的內(nèi)部原理解析)

    React、Vue中key的作用詳解 (key的內(nèi)部原理解析)

    key是虛擬DOM對(duì)象的標(biāo)識(shí),當(dāng)狀態(tài)中的數(shù)據(jù)發(fā)生變化時(shí),Vue會(huì)根據(jù)[新數(shù)據(jù)]生成[新的虛擬DOM],本文給大家介紹React、Vue中key的作用詳解 (key的內(nèi)部原理解析),感興趣的朋友一起看看吧
    2023-10-10
  • 詳解對(duì)于React結(jié)合Antd的Form組件實(shí)現(xiàn)登錄功能

    詳解對(duì)于React結(jié)合Antd的Form組件實(shí)現(xiàn)登錄功能

    這篇文章主要介紹了詳解對(duì)于React結(jié)合Antd的Form組件實(shí)現(xiàn)登錄功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • React?Fiber構(gòu)建completeWork源碼解析

    React?Fiber構(gòu)建completeWork源碼解析

    這篇文章主要為大家介紹了React?Fiber構(gòu)建completeWork源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02

最新評(píng)論

侯马市| 宜城市| 明光市| 文水县| 枝江市| 巫山县| 黎城县| 庆阳市| 万安县| 巴塘县| 文化| 临武县| 砚山县| 吴江市| 监利县| 揭阳市| 潮安县| 山阳县| 双柏县| 禄劝| 玉屏| 合作市| 文安县| 肥城市| 吉首市| 十堰市| 泰宁县| 安徽省| 昌平区| 酒泉市| 枝江市| 阿鲁科尔沁旗| 庆城县| 重庆市| 普兰县| 望谟县| 蒙山县| 靖安县| 侯马市| 南雄市| 出国|