2017-08-22 42 views
2

我的理解是,PureComponent利用shouldComponentUpdate()並對狀態和道具進行淺層比較,但我創建了一個小例子,其運行方式與我預期的不同。PureComponent如何工作?

示例應用程序顯示人員列表。每個列表項都有能力更改其名稱。試試看,並檢查代碼here

在父組件,我有一個看起來像這樣的方法:

updateName(id, newName) { 
    const { people } = this.state 
    const idx = people.findIndex(person => person.id === id) 

    people[idx].name = newName 
    this.setState({ people }) 
    } 

是我傳遞迴setState具有同一基準上一個對象的對象。在這種情況下,如果它進行淺層比較,該組件不應該更新嗎?其次,另一個不清楚的部分是,我將子組件Person從PureComponent更改爲Component,並且我仍然只從更新後的重新渲染的子項中獲益(我正在爲每個子項做一個控制檯登錄渲染,如果你想檢查)。顯然這是React在內部做的決定是否應該更新的孩子,但我認爲如果組件重新呈現它將重新呈現所有內容。

回答

2

不應該這個組件不更新,如果它進行淺比較?

是的。並沒有更新。您可以看到App組件的render()方法在初始渲染後不會再被調用。這意味着淺層比較正如您所期望的那樣工作,並且組件(正確)不會更新。

我相信這是你扔掉的部分是此行中Person.prototype.handleSubmit()

this.setState({ value: '' }) 

由於oldState.value !== newState.value,這將觸發重新繪製修改Person組件上,無論PersonPureComponent與否。

如果你把這條線拿出來,你會看到沒有更新(你期待的行爲)。

很明顯,這是React在內部做的事情,以決定是否應該更新孩子。

不,孩子正在設置自己的狀態。親子關係在這裏無關緊要。 React會直接更新孩子。

將來你應該嘗試隔離你的測試。這種混亂髮生的原因是因爲你依靠render()方法來確定一個孩子是否收到新的道具。但render()方法在孩子接收新道具並且孩子設置新狀態時被調用。

對於這種情況的一個更好的測試將是檢查是否componentWillReceiveProps()被調用,因此從畫面消除state

componentWillReceiveProps(newProps) { 
    console.log('receiving props', newProps) 
} 

如果堵塞此進Person組件,您會看到,它沒有得到調用。正如你所期待的那樣。

總之,React.PureComponent的工作原理與您的想法完全相同。

+0

優秀和徹底的答案@bowheart。一個簡單的說明,如果我在handleSubmit中添加'this.setState({value:''})'行,並且像上面那樣添加了'componentWillReceiveProps'方法,那麼當我提交時用戶的名稱會更新。你所說的狀態變化告訴渲染函數爲了更新子元素,所以即使'componentWillReceiveProps'沒有被觸發以讓孩子知道它已經更新,它也從道具上檢查更新的名稱。本質上,參考是反映變化但不通知組件。 – captDaylight

+1

@ captDaylight啊。接得好。你是對的。即使該對象不再作爲新的道具傳遞,它仍然是同一個對象。該引用的對象正在發生變化。當render()從該對象讀取name屬性時,它將會不同。這種隱式狀態依賴是React社區堅持保持數據不可變的原因。 – bowheart