shallowEqual
源码
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x, y) {
// SameValue algorithm
if (x === y) {
// Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
// Added the nonzero y check to make Flow happy, but it is redundant
return x !== 0 || y !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y;
}
}
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA, objB) {
if (is(objA, objB)) {
return true;
}
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}
var keysA = Object.keys(objA);
var keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (var i = 0; i < keysA.length; i++) {
if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
module.exports = shallowEqual;
pure-render-decorator
源码
'use strict';
var warning = require('fbjs/lib/warning');
var shallowEqual = require('fbjs/lib/shallowEqual');
/**
* Tells if a component should update given it's next props
* and state.
*
* @param object nextProps Next props.
* @param object nextState Next state.
*/
function shouldComponentUpdate(nextProps, nextState) {
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
}
/**
* Returns a text description of the component that can be
* used to identify it in error messages.
*
* @see https://github.com/facebook/react/blob/v15.4.0-rc.3/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js#L1143
* @param {function} component The component.
* @return {string} The name of the component.
*/
function getComponentName(component) {
var constructor = component.prototype && component.prototype.constructor;
return (
component.displayName
|| (constructor && constructor.displayName)
|| component.name
|| (constructor && constructor.name)
|| 'a component'
);
}
/**
* Makes the given component "pure".
*
* @param object component Component.
*/
function pureRenderDecorator(component) {
if (component.prototype.shouldComponentUpdate !== undefined) {
// We're not using the condition mecanism of warning()
// here to avoid useless calls to getComponentName().
warning(
false,
'Cannot decorate `%s` with @pureRenderDecorator, '
+ 'because it already implements `shouldComponentUpdate().',
getComponentName(component)
)
}
component.prototype.shouldComponentUpdate = shouldComponentUpdate;
return component;
}
module.exports = pureRenderDecorator;
pure-render-decorator 改成 immutable版
'use strict';
import { is } from 'immutable';
// let hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function deepEqual(objA, objB) {
if (objA === objB || is(objA, objB)) {
return true;
}
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
return false;
}
let keysA = Object.keys(objA || {});
let keysB = Object.keys(objB || {});
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
let bHasOwnProperty = Object.prototype.hasOwnProperty.bind(objB);
for (let i = 0; i < keysA.length; i++) {
if (!bHasOwnProperty(keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
/**
* Does a deep comparison for props and state.
* See ReactComponentWithPureRenderMixin
*/
export function deepCompare(instance, nextProps, nextState) {
return !deepEqual(instance.props, nextProps) || !deepEqual(instance.state, nextState);
}
/**
* Tells if a component should update given it's next props
* and state.
*
* @param object nextProps Next props.
* @param object nextState Next state.
*/
function shouldComponentUpdate(nextProps, nextState) {
return deepCompare(this, nextProps, nextState);
}
/**
* Makes the given component "pure".
*
* @param object component Component.
*/
function pureRenderDecorator(component) {
component.prototype.shouldComponentUpdate = shouldComponentUpdate;
}
export default pureRenderDecorator;
export {shouldComponentUpdate};
优化shouldComponentUpdate()
decorator方式
import {React} from 'react';
import pureRenderDecorator from 'immutable-pure-render-decorator';
@pureRenderDecorator
export default class PartA extends React.Component {
constructor(props) {
super(props);
// 舍弃React.addons.PureRenderMixin
// this.shouldComponentUpdate = React.addons.PureRenderMixin.shouldComponentUpdate.bind(this);
}
render() {
console.log('组件PartA,render执行了');
const data = this.props.data;
return (
<section>
<div>
<p>我是组件PartA</p>
<p>{data.toJSON ? JSON.stringify(data.toJSON()) : data}</p>
</div>
</section>
)
}
}
高阶组件方式
//高阶组件
import {React} from 'react';
import {shouldComponentUpdate} from 'immutable-pure-render-decorator';
export default ComposedComponent => {
return class extends React.Component {
constructor(props) {
super(props);
this.shouldComponentUpdate = shouldComponentUpdate.bind(this);
}
render() {
const props = this.props.toJS ? this.props.toJS() : this.props;
return <ComposedComponent {...this.props} {...props} />;
}
}
};
//使用高阶组件
import {React} from 'react';
import { connect } from 'react-redux';
import highComponent from '../ComposedComponent';
import actions from '../actions';
import Dialog from '../common/dialog';
// import Immutable from 'immutable';
function mapStateToProps(state) {
return {
open: state.getIn(['dialog', 'open']),
title: state.getIn(['dialog', 'title'])
}
}
function mapDispatchToProps(dispatch) {
return {
onPrimaryTouchTap: ()=> {
dispatch(actions.toggleDialog(false));
},
onSecondaryTouchTap: ()=> {
dispatch(actions.toggleDialog(false));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(highComponent(Dialog))//通过高阶组件封装