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
不能使用同一个引用哦。
生命周期地址