首頁  >  文章  >  web前端  >  使用React.setState有哪些需要注意的地方

使用React.setState有哪些需要注意的地方

亚连
亚连原創
2018-06-20 11:17:551725瀏覽

本篇文章主要介紹了淺談使用React.setState需要注意的三點,提出了三點對React 新手來說是很容易忽略的地方,具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

這篇文章原標題是3 Reasons why I stopped using React.setState ,但是我對原文作者提出的論點不是很感冒,但是作者提出的三點對React 新手來說是很容易忽略的地方,所以我在這裡只提出部分內容,而且把標題改為使用React.setState需要注意的三點。

正文

對 React 新手來說,使用 setState 是一件很複雜的事情。即使是熟練的React 開發,也很有可能因為React 的一些機製而產生一些bug,比如下面這個例子:

文檔中也說明了當使用setState 的時候,需要注意什麼問題:

注意:

絕對不要直接改變this.state ,因為之後呼叫setState() 可能會取代掉你所做的改

變。把 this.state 當做是不可變的。

setState() 不會立刻改變 this.state ,而是建立一個即將處理的 state 轉換。在呼叫該方法之後存取 this.state 可能會傳回現有的值。

對 setState 的呼叫沒有任何同步性的保證,並且呼叫可能會為了效能收益批次執行。

setState() 將總是觸發一次重繪,除非在 shouldComponentUpdate() 中實作了條件渲染邏輯。如果可變物件被使用了,但又不能在 shouldComponentUpdate() 中實現這種邏輯,僅在新 state 和之前的 state 存在差異的時候調用 setState() 可以避免不必要的重新渲染。

總結出來,當使用setState 的時候,有三個問題需要注意:

1. setState是異步的(譯者註:不保證同步的)

很多開發剛開始沒有註意到setState 是異步的。如果你修改一些 state ,然後直接查看它,你會看到之前的 state 。這是 setState 中最容易出錯的地方。 setState 這個字看起來並不像是異步的,所以如果你不假思索的用它,可能會造成 bugs 。下面這個例子很好的展示了這個問題:

class Select extends React.Component {
 constructor(props, context) {
  super(props, context)
  this.state = {
   selection: props.values[0]
  };
 }
 
 render() {
  return (
   <ul onKeyDown={this.onKeyDown} tabIndex={0}>
    {this.props.values.map(value =>
     <li
      className={value === this.state.selection ? &#39;selected&#39; : &#39;&#39;}
      key={value}
      onClick={() => this.onSelect(value)}
     >
      {value}
     </li> 
    )} 
   </ul>
  )
 }
 
 onSelect(value) {
  this.setState({
   selection: value
  })
  this.fireOnSelect()
 }

 onKeyDown = (e) => {
  const {values} = this.props
  const idx = values.indexOf(this.state.selection)
  if (e.keyCode === 38 && idx > 0) { /* up */
   this.setState({
    selection: values[idx - 1]
   })
  } else if (e.keyCode === 40 && idx < values.length -1) { /* down */
   this.setState({
    selection: values[idx + 1]
   }) 
  }
  this.fireOnSelect()
 }
  
 fireOnSelect() {
  if (typeof this.props.onSelect === "function")
   this.props.onSelect(this.state.selection) /* not what you expected..*/
 }
}

ReactDOM.render(
 <Select 
  values={["State.", "Should.", "Be.", "Synchronous."]} 
  onSelect={value => console.log(value)}
 />,
 document.getElementById("app")
)

第一眼看上去,這個程式碼似乎沒有什麼問題。兩個事件處理中呼叫 onSelect 方法。但是,這個 Select 元件中有一個 bug 很好的展現了先前的 GIF 圖。 onSelect 方法永遠傳遞的是先前的 state.selection 值,因為當 fireOnSelect 呼叫的時候, setState 還沒有完成它的工作。我認為 React 至少要把 setState 改名為 scheduleState 或把回掉函數設為必須參數。

這個bug很容易修改,最難的地方在於你要知道有這個問題。

2. setState會造成不必要的渲染

setState 造成的第二個問題是:每次呼叫都會造成重新渲染。很多時候,這些重新渲染是不必要的。你可以用 React performance tools 中的 printWasted 來查看什麼時候會發生不必要渲染。但是,大概的說,不必要的渲染有以下幾個原因:

  1. 新的 state 其實和之前的是一樣的。這個問題通常可以透過 shouldComponentUpdate 來解決。也可以用 pure render 或其他的庫賴來解決這個問題。

  2. 通常會改變的 state 是和渲染有關的,但也有例外。例如,有些數據是根據某些狀態來顯示的。

  3. 第三,有些 state 和渲染一點關係都沒有。有一些 state 可能是和事件、 timer ID 有關的。

3.setState並不能很有效的管理所有的元件狀態

基於上面的最後一條,並不是所有的元件狀態都應該用setState 來進行儲存和更新的。複雜的組件可能會有各種各樣的狀態需要管理。用 setState 來管理這些狀態不僅會造成很多不必要的重新渲染,也會造成相關的生命週期鉤子一直被調用,從而造成很多奇怪的問題。

後話

在原文作者推薦了一個叫做 MobX 的函式庫來管理部分狀態,我不是很感冒,所以我就不介紹。如果有興趣的,可以透過最上面的連結看看原文中的介紹。

基於上面提出的三點,我認為新手應該注意的地方是:

setState 是不保證同步的

setState 是不保證同步的,是不保證同步的,是不保證同步的。重要的事情說三遍。之所以不說它是異步的,是因為 setState 在某些情況下也是同步更新的。可以參考這篇文章

如果需要在 setState 後面直接取得修改後的值,那麼有幾個方案:

傳入對應的參數,不透過 this.state 取得

针对于之前的例子,完全可以在调用 fireOnSelect 的时候,传入需要的值。而不是在方法中在通过 this.state 来获取

使用回调函数

setState 方法接收一个 function 作为回调函数。这个回掉函数会在 setState 完成以后直接调用,这样就可以获取最新的 state 。对于之前的例子,就可以这样:

this.setState({
 selection: value
}, this.fireOnSelect)

使用setTimeout

在 setState 使用 setTimeout 来让 setState 先完成以后再执行里面内容。这样子:

this.setState({
 selection: value
});
setTimeout(this.fireOnSelect, 0);

直接输出,回调函数, setTimeout 对比

componentDidMount(){
  this.setState({val: this.state.val + 1}, ()=>{
   console.log("In callback " + this.state.val);
  });

  console.log("Direct call " + this.state.val);  
  setTimeout(()=>{
   console.log("begin of setTimeout" + this.state.val);
    this.setState({val: this.state.val + 1}, ()=>{
     console.log("setTimeout setState callback " + this.state.val);
    });

   setTimeout(()=>{
    console.log("setTimeout of settimeout " + this.state.val);
   }, 0);

   console.log("end of setTimeout " + this.state.val);
  }, 0);
 }

如果val默认为0, 输入的结果是:

Direct call 0
In callback 1
begin of setTimeout 1
setTimeout setState callback 2
end of setTimeout 2
setTimeout of settimeout 2

和渲染无关的状态尽量不要放在 state 中来管理

通常 state 中只来管理和渲染有关的状态 ,从而保证 setState 改变的状态都是和渲染有关的状态。这样子就可以避免不必要的重复渲染。其他和渲染无关的状态,可以直接以属性的形式保存在组件中,在需要的时候调用和改变,不会造成渲染。

避免不必要的修改,当 state 的值没有发生改变的时候,尽量不要使用 setState 。虽然 shouldComponentUpdate 和 PureComponent 可以避免不必要的重复渲染,但是还是增加了一层 shallowEqual 的调用,造成多余的浪费。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在vue中使用axios实现文件上传

使用gulp如何创建完整的项目流程

在js中如何实现将数组添加到对象中

在jQuery中如何实现动态控制页面元素

在canvas中如何实现轨迹回放功能

以上是使用React.setState有哪些需要注意的地方的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn