簡體   English   中英

反應JS setState行為

[英]React JS setState behaviour

我正在學習本教程 ,並在1:10:18(鏈接中包含時間步長)上,他寫了this.setState({ count: this.state.count + 1 }); (其中count是狀態對象內的原始值,單擊按鈕后將顯示在頁面上)從導師的話中,我了解到我們需要在setState內部指定count:以便React將在狀態對象中更新此值。 但是我注意到我可以在setState中寫一些廢話,例如:

  handleIncrement = key => {
    this.state.totalCount += 1;
    this.state.tags[key] += 1;
    this.setState({ countBlabla: 23213123 });
  };

而且HTML仍將正確更新,而無需重新呈現所有內容。 (我已經簽入開發人員工具,只有已更改的值會更新,其他所有內容都保持不變)

我也嘗試過: this.setState({}); 效果很好。 但是this.setState(); 才不是。

那么將setState傳遞給參數有什么意義呢?

您可以直接寫入this.state唯一位置應該是Components構造函數。 在所有其他地方,您應該使用this.setState函數,該函數將接受一個Object,該Object最終將合並到Components當前狀態。

盡管在技術上可以通過直接寫入this.state來更改狀態,但是這不會導致使用新數據重新渲染組件,並且通常會導致狀態不一致。

另外, this.setState()是一個異步函數,因此,如果您只是在調用新狀態后嘗試console.log()新狀態,就不會立即看到更改。 相反,您可以傳遞一個回調函數this.setState({var: newVar}, () => {console.log(this.state)})

遵循以下行react軟件包的代碼:

setState方法只調用enqueueSetState方法。 而這又使特定反應組件的更新入隊。 更新是通過react-dom包(或react-native )處理的

如果您看一下forceUpdate方法,您會發現它實際上在做同樣的事情(調用enqueueSetState )。 而且此方法僅觸發對帳過程。

因此,簡而言之,沒有必要從純觸發前瞻性的狀態中改變狀態,也可以不改變狀態。 但是從優化和清晰的數據流角度出發,這是非常必要的。 如果在使用setState時依賴state引用,則當某些組件看到舊狀態時,您可能會遇到棘手的行為。

您只需要傳遞需要更新的值。 React會將當前狀態傳播到新狀態。 如果您現在的狀態為{ count: 1, prop: 2 }

// if you will make 
this.setState({ count: 11 })

// react will do
{ ...currentState, count: 11 }

// and result will be
{ prop: 2, count: 11 }

並通過調用對帳過程請求更新組件。 就是這樣,不要忘記:

只是不要改變狀態:)

PS 是Dan Abramov(React團隊的核心成員)的精彩文章

通常,您永遠不想直接改變狀態。 始終使用setState

在回答你的問題,為什么我們需要指定鍵(在您的示例 ):因為這是我們要改變。 setState不僅會重新渲染組件,而且我們也不只是為了重新渲染而指定鍵。 它更改狀態,然后重新渲染組件,以便可以使用狀態中的新值。

從技術上講,您可以forceUpdate狀態並使用隨機參數或對象調用forceUpdatesetState ,但您不應這樣做,因為這會破壞React組件設計的自然工作流程。

如果將對象傳遞給setState,它將在內部合並該對象並導致重新渲染;如果未傳遞任何內容,它將跳過該重新渲染,這是您觀察到的行為

現在讓我們看一下上面代碼的缺點

handleIncrement = key => {
    this.state.totalCount += 1;
    this.state.tags[key] += 1;
    this.setState({ countBlabla: 23213123 });
  };

在上述情況下,假設您將totalCount和標簽作為道具傳遞給子組件,並基於要進行API調用的totalCount更改或標簽更改在子組件中傳遞。 根據React流,您可以在componentDidUpdate生命周期方法中執行此操作

componentDidUpdate(prevProps) {
    if (prevProps.totalCount !== this.props.totalCount) {
        // make api cal;
    }
}

但是,盡管以上代碼在totalCount為整數,布爾值或字符串的情況下都可以使用,因為它們是不可變的,但不適用於對象。 這樣的情況將很難調試,並可能導致意外的行為。

您正在做的事情叫做巧合編程。

您的代碼恰好由於react的內部工作而起作用,但是它是未記錄的行為,將來可能會在沒有警告的情況下停止工作。

建議的方法是使用this.setState({...})並傳遞對狀態所做的所有更改的對象。 您的情況是:

  this.setState({ 
      totalCount: this.state.totalCount + 1 
      tags: {
           ...this.state.tags,
          [key]: this.state.tags[key] + 1 
       }  
  });

這將更新狀態並獲得與您的代碼相同的副作用。

但是 ,將當前狀態視為不可變是很重要的,因為例如,如果將來某個時候您決定實施shouldComponentUpdate方法並拒絕狀態更改(如果手動更改了狀態),由於先前的不一致,將會產生一些意想不到的后果陳述您的期望。

除其他答案外,如果要訪問先前的狀態,還可以將其作為參數傳遞給setState。

例如:

 this.setState((prevState) => ({
      totalCount: prevState.totalCount + 1 
      tags: {
           ...prevState.tags,
          [key]: prevState.tags[key] + 1 
       }  
  }));

建議這樣做,因為此狀態可能會異步更新。

更多信息: https//reactjs.org/docs/state-and-lifecycle.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM