組件的狀態是一種保存、處理和使用給定組件內部信息的方法,並容許你實現其自身的邏輯。狀態自己實際上是JavaScript中一個簡單的對象(Plain Old Java[Script] Object),而且改變它是使組件從新進行渲染的幾種方法之一。異步
這是React背後最基本的思路之一,可是它(狀態)有一些使用起來很棘手的屬性,可能會致使應用程序出現意外行爲。函數
組件中的構造函數是惟一一個你能夠直接寫this.state
的地方,而在其餘地方你應該使用this.setState
,setState
將接受最終合併到組件當前狀態的一個對象或方法做爲參數。post
雖然技術上能夠經過直接寫入this.state
來改變狀態,但它不會致使組件使用新數據從新渲染,而且一般會致使狀態的不一致。this
setState致使協調(從新渲染組件樹的過程)的事實是基於下一個屬性 — setState是異步的。這容許咱們在單個範圍內屢次調用setState,而不是觸發不須要從新渲染整個組件樹。code
這就是爲何在更新後沒有在狀態中看到新值的緣由。component
// assuming this.state = { value: 0 } this.setState({ value: 1 }); console.log(this.state.value); // 0
React還會嘗試將setState分組調用或批量調用到一個回調中,這會致使咱們第一次「陷阱」。對象
// assuming this.state = { value: 0 }; this.setState({ value: this.state.value + 1}); this.setState({ value: this.state.value + 1}); this.setState({ value: this.state.value + 1});
上面全部的調用過程結束後,this.state.value
的值是1,而不是咱們所指望的3。爲了解決這個問題 …ip
若是你在setState中傳入一個函數做爲第一個參數,React將以 at-call-time-current狀態來調用它,並指望你返回一個對象來合併到狀態中。因此更新咱們以上的代碼:get
// assuming this.state = { value: 0 }; this.setState((state) => ({ value: state.value + 1})); this.setState((state) => ({ value: state.value + 1})); this.setState((state) => ({ value: state.value + 1}));
最終的結果將如咱們所指望的this.state.value = 3
,記住在將狀態更新爲值時始終使用此語法,該值是根據之前的狀態計算的!回調函數
記住你是如何知道setState是異步的?嗯,事實證實並不是老是如此!這取決於執行上下文,例如:
render() { return <button onClick={this.inc}>Click to update</button> } inc() { console.log('before: ' + this.state.test); this.setState({ test: this.state.test+1 }); console.log('after: ' + this.state.test); }
點擊按鈕,而後在控制檯的顯示結果多是:
// click! before: 1 after: 1 // click! before: 2 after: 2
可是若是咱們新增如下代碼:
componentDidMount() { setInterval(this.inc, 1000); }
結果是:
before: 1 after: 2 before: 2 after: 3
因此,咱們應該學會什麼時候期待什麼行爲嗎?顯然不是。能夠確定的是,假設setState
確實是異步的,由於在未來的版本中它將是如此。
若是你須要執行某個函數,或驗證狀態確實是否正確更新,你能夠傳遞一個函數做爲setState
調用的第二個參數,一旦狀態更新,該函數將被執行。記住,由於在一個範圍內的全部更新是批量的,若是你有屢次調用setState
,則將使用徹底更新後的狀態調用每一個回調。
在更新發生後確保代碼執行的另外一種方法是將其放在componentWillUpdate
或componentDidUpdate
中。然而,當使用shouldComponentUpdate
來阻止更新的時候,相反上兩個鉤子函數是不用去使用的。
其中最多見的一個錯誤是在構造函數中基於屬性(props)來使用組件的state設置它的值。考慮如下的代碼:
class Component extends React.Component { constructor(props) { super(props); this.state = { value: this.props.value }; } render() { return <div>The value is: {this.state.value}</div> } }
若是其父組件以下面這樣對它進行渲染:
<Component value={42} />
將正確渲染「The value is 42」,若是父組件改變了
<Component value={13} />
最後this.state.value
的值依然是42,這是由於React並不會銷燬和從新建立這個組件,它將重用一次渲染的組件,不會從新運行構造函數。爲了解決這個,你不該將props分配給state而是在render方法中使用this.props.value
。然而若是你決定使用state,你應該實現一個解決方案,它將在須要時更新狀態,例如:
class Component extends React.Component { constructor(props) { super(props); this.state = { value: this.props.value }; } componentWillReceiveProps(nextProps) { if(nextProps.value !== this.props.value) { this.setState({value: nextProps.value}); } } render() { return <div>The value is: {this.state.value}</div> } }
記住任何componentWill *
函數都不是觸發反作用的地方(例如進行AJAX調用),因此對於上面的狀況請使用componentDidUpdate(previousProps, previousState)
。
咱們能夠預期setState會隨着React Fiber及其餘更改而發生一些變化。正如前面提到的,在大多數狀況下setState
是異步的,
另外一個變化是使用函數語法能夠終止「進行中」的setState
調用:
this.setState((state) => { if(checkSomeConditions()) return undefined; else return { value: 42} });