React.Component
class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
在 React
組件中,代碼重用的主要方式是組合而不是繼承。react
在 React.Component 的子類中有個必須定義的 render() 函數。本章節介紹其餘方法均爲可選。
React.PureComponent
React.PureComponent
與 React.Component
很類似。二者的區別在於 React.Component
並未實現 shouldComponentUpdate()
,而 React.PureComponent
中以淺層對比prop
和 state
的方式來實現了該函數。web
若是賦予 React
組件相同的 props
和 state,render()
函數會渲染相同的內容,那麼在某些狀況下使用 React.PureComponent
可提升性能。數組
React.PureComponent
中的 shouldComponentUpdate()
僅做對象的淺層比較。若是對象中包含複雜的數據結構,則有可能由於沒法檢查深層的差異,產生錯誤的比對結果。僅在你的 props
和 state
較爲簡單時,才使用 React.PureComponent
,或者在深層數據結構發生變化時調用 forceUpdate() 來確保組件被正確地更新。你也能夠考慮使用 immutable
對象加速嵌套數據的比較。數據結構
此外,React.PureComponent
中的 shouldComponentUpdate()
將跳過全部子組件樹的 prop
更新。所以,請確保全部子組件也都是「純」的組件。函數
當組件更新時,若是組件的 props
和 state
都沒發生改變, render
方法就不會觸發,省去 Virtual DOM
的生成和比對過程,達到提高性能的目的。具體就是 React
自動幫咱們作了一層淺比較:性能
if (this._compositeType === CompositeTypes.PureClass) { shouldUpdate = !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState); }
而 shallowEqual
又作了什麼呢?會比較 Object.keys(state | props)
的長度是否一致,每個 key
是否二者都有,而且是不是一個引用,也就是隻比較了第一層的值,確實很淺,因此深層的嵌套數據是對比不出來的。this
function shallowEqual(objA: mixed, objB: mixed): boolean { if (is(objA, objB)) { return true; } if ( typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null ) { return false; } const keysA = Object.keys(objA); const keysB = Object.keys(objB); if (keysA.length !== keysB.length) { return false; } // Test for A's keys different from B. for (let i = 0; i < keysA.length; i++) { if ( !hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]]) ) { return false; } } return true; } export default shallowEqual; function is(x: any, y: any) { return ( (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare ); } // Object.is()是在ES6中定義的一個新方法,它與‘===’相比,特別針對-0、+0、NaN作了處理。Object.is(-0, +0)會返回false,而Object.is(NaN, NaN)會返回true。這與===的判斷剛好相反,也更加符合咱們的預期。
https://www.imweb.io/topic/59...
class App extends PureComponent { state = { items: [1, 2, 3] } handleClick = () => { const { items } = this.state; items.pop(); this.setState({ items }); } render() { return (<div> <ul> {this.state.items.map(i => <li key={i}>{i}</li>)} </ul> <button onClick={this.handleClick}>delete</button> </div>) } }
會發現,不管怎麼點 delete
按鈕, li
都不會變少,由於 items 用的是一個引用, shallowEqual
的結果爲 true
。改正:spa
handleClick = () => { const { items } = this.state; items.pop(); this.setState({ items: [].concat(items) }); }
若是是基本類型, 是可以更新的.
引用類型,則不會更新.eslint
handleClick = () => { const { items } = this.state; items.splice(items.length - 1, 1); this.setState({ items }); }
子組件裏仍是re-render了。這樣就須要咱們保證不變的子組件數據的引用不能改變。這個時候能夠使用immutable-js
函數庫。code
// 1 <MyInput onChange={e => this.props.update(e.target.value)} /> // 2 update(e) { this.props.update(e.target.value) } render() { return <MyInput onChange={this.update.bind(this)} /> }
因爲每次 render
操做 MyInput
組件的 onChange
屬性都會返回一個新的函數,因爲引用不同,因此父組件的 render 也會致使 MyInput 組件的 render
,即便沒有任何改動,因此須要儘可能避免這樣的寫法,最好這樣寫:
// 1,2 update = (e) => { this.props.update(e.target.value) } render() { return <MyInput onChange={this.update} /> }
有時候後臺返回的數據中,數組長度爲0或者對象沒有屬性會直接給一個 null
,這時候咱們須要作一些容錯:
class App extends PureComponent { state = { items: [{ name: 'test1' }, null, { name: 'test3' }] } store = (id, value) => { const { items } = this.state; items[id] = assign({}, items[id], { name: value }); this.setState({ items: [].concat(items) }); } render() { return (<div> <ul> {this.state.items.map((i, k) => <Item style={{ color: 'red' }} store={this.store} key={k} id={k} data={i || {}} />) } </ul> </div>) } }
PureComponent
真正起做用的,只是在一些純展現組件上,複雜組件用了也不要緊,反正 shallowEqual
那一關就過不了,不過記得 props
和 state
不能使用同一個引用哦。
生命週期地址