国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【譯】你可能不需要派生狀態(tài)

dinfer / 2664人閱讀

摘要:所有派生狀態(tài)導(dǎo)致的問題無異于兩種無條件的根據(jù)來更新無論和是否匹配來更新。派生狀態(tài)最常見的錯(cuò)誤就是將這兩者混和在一起。因此通常被用于性能優(yōu)化而不是來判斷派生狀態(tài)的正確性。我們可以使用派生狀態(tài)來存儲(chǔ)過濾列表這種方式避免了重新計(jì)算。

原文鏈接:https://reactjs.org/blog/2018...

翻譯這篇文章的起因是因?yàn)樵谝淮涡枨蟮绣e(cuò)誤的使用了getDerivedStateFromProps這個(gè)生命周期導(dǎo)致子組件的state被循環(huán)重置,于是翻到了這篇文章,然后就開啟的翻譯之旅。

在很長(zhǎng)一段時(shí)間,生命周期componentWillReceiveProps是用來響應(yīng)props更新來改變state并不需要額外渲染的唯一方法。在16.3版本中,我們提供了getDerivedStateFromProps這個(gè)更安全生命周期來解決相同的用例。同時(shí),我們發(fā)現(xiàn)人們對(duì)于如何使用這兩種方式有很多誤解,并且我們發(fā)現(xiàn)了一些造成微妙和令人混淆的反模式。在16.4中的getDerivedStateFromProps的bug修復(fù)使得派生狀態(tài)更加可預(yù)測(cè),且更容易讓人注意到錯(cuò)誤使用它的結(jié)果。

什么時(shí)候去使用派生狀態(tài)

getDerivedStateFromProps的存在只有一個(gè)目的。它可以使組件根據(jù)props的改變來更新內(nèi)部的state。我們之間的博客提供了一些例子:通過改變offset的prop來改變當(dāng)前的滾動(dòng)方向和加載通過source props所指定的外部數(shù)據(jù)。

我們沒有提供更多的例子,因?yàn)樽鳛橐粋€(gè)基本的規(guī)則,派生狀態(tài)應(yīng)該被謹(jǐn)慎的使用。所有派生狀態(tài)導(dǎo)致的問題無異于兩種:(1)無條件的根據(jù)props來更新state(2)無論props和state是否匹配來更新state。

如果僅用派生狀態(tài)來記錄一些基于當(dāng)前props的計(jì)算,則不需要派生狀態(tài);

如果你無條件的更新派生狀態(tài),或者無論props和state是否匹配來更新state,你的組件將會(huì)過于頻繁的去重置狀態(tài);

使用派生狀態(tài)的常見問題

“受控的”和“不受控的”通常用來指表單的輸入,但它也同樣可以表示任何組件數(shù)據(jù)所在的位置。數(shù)據(jù)通過props傳來被認(rèn)為是“受控的”(因?yàn)楦附M件在控制著這個(gè)數(shù)據(jù))。數(shù)據(jù)僅存在其內(nèi)部的state中被認(rèn)為是“不受控的”(因?yàn)槠涓附M件不能直接的改變這它)。

派生狀態(tài)最常見的錯(cuò)誤就是將這兩者混和在一起。當(dāng)一個(gè)派生狀態(tài)的值同樣通過setState的調(diào)用來更新時(shí),這就無法保證數(shù)據(jù)有單一的真實(shí)來源。這也許和上面提到的外部數(shù)據(jù)加載的例子很相似,但他們?cè)谝恍┲匾姆矫嫔鲜遣煌摹T诩虞d的例子中,”source“的props和”loading“的state都有一個(gè)明確的真實(shí)來源。當(dāng)source props改變的時(shí)候,應(yīng)該總是覆蓋loading state。相反,只有props改變且由組件管理的時(shí)候,才去重寫state。

當(dāng)這些約束中的任何一個(gè)被改變時(shí)將會(huì)出現(xiàn)問題。通常有兩種形式,讓我們接下來看一下這兩種形式。

反模式:無條件的從prop復(fù)制狀態(tài)到state

一個(gè)常見的誤解是getDerivedStateFromPropscomponentWillReceiveProps只有在props改變的時(shí)候會(huì)被調(diào)用。這兩個(gè)生命周期將會(huì)在父組件重新渲染的任何時(shí)間被調(diào)用,而不管props是否與之前不同。因此,在使用這兩個(gè)生命周期時(shí),無條件的覆蓋state總是不安全的,將會(huì)導(dǎo)致state更新時(shí)的丟失

讓我們考慮一個(gè)例子來說明這個(gè)問題。

class EmailInput extends Component {
  state = { email: this.props.email };

  render() {
    return ;
  }

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  componentWillReceiveProps(nextProps) {
    // 這里將會(huì)覆蓋任何本地state的更新。
    this.setState({ email: nextProps.email });
  }
}

這個(gè)組件看起來可能沒有問題,state由prop傳來的數(shù)據(jù)初始化,且當(dāng)我們改變input的值時(shí)state被更新。但是當(dāng)我們的父組件重新渲染的時(shí)候,任何我們?cè)趇nput中輸入的狀態(tài)都將丟失,即使我們?nèi)ケ容^nextProps.email !== this.state.email也是如此。

在這個(gè)例子中,只有當(dāng)email的prop的改變的時(shí)候添加shouldComponentUpdate來重新渲染可以解決這個(gè)問題,但是實(shí)際上,組件通常會(huì)接收多個(gè)prop,另一個(gè)prop的改變?nèi)稳粫?huì)造成組件的重新渲染和不正確的重置。此外函數(shù)和對(duì)象的prop通常也會(huì)是內(nèi)聯(lián)創(chuàng)建的,這也會(huì)使shouldComponentUpdate正確的返回true變得困難。這里有一個(gè)例子。因此shouldComponentUpdate通常被用于性能優(yōu)化而不是來判斷派生狀態(tài)的正確性。

希望到現(xiàn)在大家清楚為什么不要無條件的復(fù)制props到state。在我們找到可能的解決方案之前,讓我們?nèi)タ匆粋€(gè)與之相關(guān)的問題:如果只在props.email改變的時(shí)候去更新state會(huì)怎樣?

反模式:props改變的時(shí)候清除state

繼續(xù)上面的例子,我們可以避免在props.email更改時(shí)意外的清除state:

class EmailInput extends Component {
  state = {
    email: this.props.email
  };

  componentWillReceiveProps(nextProps) {
    // 任何時(shí)候props.email改變,更新state.
    if (nextProps.email !== this.props.email) {
      this.setState({
        email: nextProps.email
      });
    }
  }
  
  // ...
}

我們?nèi)〉昧撕艽蟮倪M(jìn)步,現(xiàn)在我們的組件只有在props真正改變的時(shí)候才會(huì)清除state。

還有一個(gè)微妙的問題,想象一下使用以上的組件來構(gòu)建密碼管理應(yīng)用。當(dāng)使用同一個(gè)email在兩個(gè)賬戶的詳情頁(yè)導(dǎo)航時(shí),input將會(huì)無法重置,這是因?yàn)閭鬟f給組件的props相對(duì)于兩個(gè)賬號(hào)來說時(shí)相同的。這對(duì)用戶來說將會(huì)是一個(gè)驚喜,因?yàn)閷?duì)一個(gè)賬戶的未保存更改會(huì)錯(cuò)誤的影響到另一個(gè)賬戶。查看演示。

這種設(shè)計(jì)從本質(zhì)上來說是錯(cuò)誤的,但卻是一個(gè)很容易犯的錯(cuò)誤,幸運(yùn)的是,有兩種更好的選擇,這兩者的關(guān)鍵在于,對(duì)于任何數(shù)據(jù)片段,你都需要選擇一個(gè)將它作為數(shù)據(jù)源的組件,而避免在其它組件重復(fù)使用。

首選方案 推薦:完全受控組件

避免上述問題的一個(gè)方案是完全移除組建中的state,如果email僅作為props存在,那我們將不必?fù)?dān)心它和state沖突,我們甚至可以講EmailInput組件變?yōu)楦p量級(jí)的function組件:

function EmailInput(props) {
  return ;
}

這種方法簡(jiǎn)化了組件的實(shí)現(xiàn),但如果你仍需要存儲(chǔ)一個(gè)草稿值,那么父表單組件現(xiàn)在需要手動(dòng)執(zhí)行該操作。查看演示。

推薦:帶有key的完全不受控組件

另一個(gè)方法是讓我們的組件完全擁有“草稿”email的state,這時(shí)我們的組件仍然可以接收props來作為初始值,但是它會(huì)忽略props的后續(xù)更改。

class EmailInput extends Component {
  state = { email: this.props.defaultEmail };

  handleChange = event => {
    this.setState({ email: event.target.value });
  };

  render() {
    return ;
  }
}

為了在移動(dòng)到其他項(xiàng)目時(shí)重置值(如密碼管理器場(chǎng)景中),可以使用一個(gè)React的特殊屬性key當(dāng)一個(gè)key改變的時(shí)候,React會(huì)創(chuàng)建一個(gè)新的組件實(shí)例而不是更新當(dāng)前的組件key通常被用在動(dòng)態(tài)的list但是同樣可以在這里使用。

每當(dāng)id改變的時(shí)候,EmailInput組件將會(huì)被重新創(chuàng)建,它的state將會(huì)被重置為最后一次的defaultEmail的值。查看演示。使用此方法,你講不用在每一個(gè)input上添加key,把一個(gè)key放在整個(gè)form上更有意義,每當(dāng)key改變的時(shí)候,表單中的input都會(huì)重置到其初始狀態(tài)。

替代方案1:通過ID prop來重置不受控組件

如果key在某些場(chǎng)合不適用(也許初始化對(duì)于組件來說是昂貴的),一個(gè)可行但繁瑣的方式是在getDerivedStateFromProps中去監(jiān)測(cè)userID:

class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail,
    prevPropsUserID: this.props.userID
  };

  static getDerivedStateFromProps(props, state) {
    if (props.userID !== state.prevPropsUserID) {
      return {
        prevPropsUserID: props.userID,
        email: props.defaultEmail
      };
    }
    return null;
  }

  // ...
}

這也提供了靈活性,如果我們選擇,只重置組件內(nèi)的部分state。查看演示。

替代方案2:通過實(shí)例方法來重置不受控組件

如果沒有合適的id來作為key但是又要重置狀態(tài),一種解決方案是為組件生成一個(gè)隨機(jī)數(shù)或者自動(dòng)遞增值來作為key,另一種方案是通過實(shí)例的方法來強(qiáng)制重置組件的state。

class EmailInput extends Component {
  state = {
    email: this.props.defaultEmail
  };

  resetEmailForNewUser(newEmail) {
    this.setState({ email: newEmail });
  }

  // ...
}

父組件將通過ref拿到組件的實(shí)例從而調(diào)用該方法。查看演示。

在某些場(chǎng)景下ref會(huì)很有用,但是我們建議你謹(jǐn)慎的使用它,即使在demo中,這個(gè)方法也是最不理想的,因?yàn)閷?huì)造成兩次渲染而不是一個(gè)。

總結(jié)

總而言之,當(dāng)設(shè)計(jì)一個(gè)組件時(shí),一個(gè)重要的方面是它的數(shù)據(jù)是可控的還是不可控的。

盡量避免在state中去“鏡像”一個(gè)props值,使這個(gè)組件成為受控組件,在父組件的state中去合并這兩個(gè)state。例如,與其在組件中去接受一個(gè)committed的props并且跟蹤一個(gè)draft的state,不如讓父組件去同時(shí)管理這個(gè)state.draftValue和state.committedValue并直接控制子組件,這將使組件更加的明確和可預(yù)測(cè)。

對(duì)于一個(gè)不受控組件,如果你想根據(jù)一個(gè)props的改變來重置state,你需要遵循以下幾點(diǎn):

首選:要重置全部?jī)?nèi)部state,使用key屬性;

備選1:如果只重置部分state,監(jiān)測(cè)props中屬性的變化;

備選2:還可以考慮通過ref調(diào)用實(shí)力的方法;

memoization怎樣?

我們還看到了派生狀態(tài)用于確保渲染中使用的昂貴值僅在輸入發(fā)生變化時(shí)才會(huì)重新計(jì)算,這種技術(shù)叫做memoization

使用派生狀態(tài)來做memoization不一定是壞事,但通常不是最好的解決辦法。派生狀態(tài)的管理存在一定的復(fù)雜性,并且這種復(fù)雜性隨著屬性的增加而增加。例如,如果我們向組件的state添加第二個(gè)派生字段,那么我們的實(shí)現(xiàn)將需要分別跟蹤對(duì)兩個(gè)字段的更改。

讓我們看一個(gè)組件的示例,該組件使用一個(gè)prop(項(xiàng)目列表)并呈現(xiàn)與用戶輸入的搜索查詢匹配的項(xiàng)。我們可以使用派生狀態(tài)來存儲(chǔ)過濾列表:

class Example extends Component {
  state = {
    filterText: "",
  };

  // *******************************************************
  // NOTE: this example is NOT the recommended approach.
  // See the examples below for our recommendations instead.
  // *******************************************************

  static getDerivedStateFromProps(props, state) {
    // Re-run the filter whenever the list array or filter text change.
    // Note we need to store prevPropsList and prevFilterText to detect changes.
    if (
      props.list !== state.prevPropsList ||
      state.prevFilterText !== state.filterText
    ) {
      return {
        prevPropsList: props.list,
        prevFilterText: state.filterText,
        filteredList: props.list.filter(item => item.text.includes(state.filterText))
      };
    }
    return null;
  }

  handleChange = event => {
    this.setState({ filterText: event.target.value });
  };

  render() {
    return (
      
        
        
    {this.state.filteredList.map(item =>
  • {item.text}
  • )}
); } }

這種方式避免了重新計(jì)算filteredList。但是他比我們需要的更加的復(fù)雜,因?yàn)樗枰謩e的跟蹤和檢查我們的props和state以便能夠正確的更新列表。在下面這個(gè)例子中,我們通過PureComponent并將filter操作放到render中來簡(jiǎn)化操作:

// PureComponents只有在至少一個(gè)state或者prop改變的時(shí)候才會(huì)重新渲染
// 通過對(duì)state和props的keys的淺比較來確認(rèn)改變。
class Example extends PureComponent {
  state = {
    filterText: ""
  };

  handleChange = event => {
    this.setState({ filterText: event.target.value });
  };

  render() {
    // 只有props.list 或 state.filterText 改變的時(shí)候PureComponent的render才會(huì)調(diào)用
    const filteredList = this.props.list.filter(
      item => item.text.includes(this.state.filterText)
    )

    return (
      
        
        
    {filteredList.map(item =>
  • {item.text}
  • )}
); } }

上述例子比派生狀態(tài)的版本更加的干凈和簡(jiǎn)潔,但是有些時(shí)候這可能還不夠好,例如對(duì)于大型列表來說,過濾可能很慢,且如果有其他的props改變PureComponent也不會(huì)阻止其重新渲染。為了解決這兩個(gè)問題,我們可以添加一個(gè)memoization,以避免不必要地重新過濾我們的列表:

import memoize from "memoize-one";

class Example extends Component {
  state = { filterText: "" };

  filter = memoize(
    (list, filterText) => list.filter(item => item.text.includes(filterText))
  );

  handleChange = event => {
    this.setState({ filterText: event.target.value });
  };

  render() {
    const filteredList = this.filter(this.props.list, this.state.filterText);

    return (
      
        
        
    {filteredList.map(item =>
  • {item.text}
  • )}
); } }

當(dāng)使用memoization時(shí),有以下約束:

在大多數(shù)情況下,您需要將memoized函數(shù)附加到組件實(shí)例。這可以防止組件的多個(gè)實(shí)例重置彼此的memoized key。

通常情況下,您需要使用具有有限緩存大小的memoization,以防止內(nèi)存泄漏。(在上面的例子中,我們使用了memoize-one,因?yàn)樗痪彺孀罱膮?shù)和結(jié)果。)

如果每次父組件呈現(xiàn)時(shí)重新創(chuàng)建props.list,本節(jié)中顯示的實(shí)現(xiàn)都不會(huì)起作用。但在大多數(shù)情況下,這種設(shè)置是合適的。

最后

在實(shí)際應(yīng)用中,組件通常包含受控和不受控制行為混合。沒關(guān)系,如果每個(gè)值都有明確的來源,則可以避免上面提到的反模式。

值得重新思考的是,getDerivedStateFromProps(以及通常的派生狀態(tài))是一種高級(jí)功能,應(yīng)該謹(jǐn)慎使用。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/99313.html

相關(guān)文章

  • 】為什么 React16 對(duì)開發(fā)人員來說是一種福音

    摘要:譯者前端小智原文就像人們對(duì)更新移動(dòng)應(yīng)用程序和操作系統(tǒng)感到興奮一樣,開發(fā)人員也應(yīng)該對(duì)更新框架感到興奮。錯(cuò)誤邊界是一種組件。注意將作為值傳遞進(jìn)去并不會(huì)導(dǎo)致使用。如果兩者不同,則返回一個(gè)用于更新狀態(tài)的對(duì)象,否則就返回,表示不需要更新狀態(tài)。 譯者:前端小智 原文:medium.freecodecamp.org/why-react16… 就像人們對(duì)更新移動(dòng)應(yīng)用程序和操作系統(tǒng)感到興奮一樣,開發(fā)人員也應(yīng)...

    kbyyd24 評(píng)論0 收藏0
  • 】Redux 還是 Mobx,讓我來解決的困惑!

    摘要:我現(xiàn)在寫的這些是為了解決和這兩個(gè)狀態(tài)管理庫(kù)之間的困惑。這甚至是危險(xiǎn)的,因?yàn)檫@部分人將無法體驗(yàn)和這些庫(kù)所要解決的問題。這肯定是要第一時(shí)間解決的問題。函數(shù)式編程是不斷上升的范式,但對(duì)于大部分開發(fā)者來說是新奇的。規(guī)模持續(xù)增長(zhǎng)的應(yīng) 原文地址:Redux or MobX: An attempt to dissolve the Confusion 原文作者:rwieruch 我在去年大量的使用...

    txgcwm 評(píng)論0 收藏0
  • []每位開發(fā)者都應(yīng)該知道SOLID原則

    摘要:開閉原則軟件實(shí)體類,模塊,函數(shù)應(yīng)該是可以擴(kuò)展的,而不是修改。函數(shù)并不符合開閉原則,因?yàn)橐坏┯行聞?dòng)物出現(xiàn),它需要修改代碼。 By Chidume Nnamdi | Oct 9, 2018 原文 面向?qū)ο蟮木幊填愋蜑檐浖_發(fā)帶來了新的設(shè)計(jì)。 這使開發(fā)人員能夠在一個(gè)類中組合具有相同目的/功能的數(shù)據(jù),來實(shí)現(xiàn)單獨(dú)的一個(gè)功能,不必關(guān)心整個(gè)應(yīng)用程序如何。 但是,這種面向?qū)ο蟮木幊踢€是會(huì)讓開發(fā)者困惑或...

    go4it 評(píng)論0 收藏0
  • Java? 教程(繼承)

    繼承 在前面的課程中,你已經(jīng)多次看到了繼承,在Java語言中,類可以從其他類派生,從而從這些類繼承字段和方法。 定義:從另一個(gè)類派生的類稱為子類(也是派生類,擴(kuò)展類或子類),派生子類的類稱為超類(也是基類或父類)。 除了Object沒有超類,每個(gè)類都有一個(gè)且只有一個(gè)直接超類(單繼承),在沒有任何其他顯式超類的情況下,每個(gè)類都隱式地是Object的子類。 類可以從派生自類的類派生的類派生,依此類推,...

    Achilles 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<