Home Reference Source Repository

src/index.js

import React from 'react';
import keys from 'lodash.keys';
import { is } from 'immutable';
import { getComponentName } from './util';

/**
 * @external {React.Component} https://facebook.github.io/react/docs/component-api.html
 */

/**
 * Simply checks if a component update is needed
 * @public
 * @param {object} context - the component context, containing the current props and state values
 * @param {object} nProps - the next props in the update process
 * @param {object} nState - the next state in the update process
 * @return {boolean} - true if should update the component else false
 * @since 1.0.0
 */
export function shouldUpdate({ props, state }, nProps, nState) {
  const propsKeys = keys(props || {});
  const stateKeys = keys(state || {});
  const nPropsKeys = keys(nProps || {});
  const nStateKeys = keys(nState || {});
  if (propsKeys.length !== nPropsKeys.length || stateKeys.length !== nStateKeys.length) {
    return true;
  }
  for (let i = 0, l = propsKeys.length; i < l; i += 1) {
    const key = propsKeys[i];
    if (!Object.prototype.hasOwnProperty.call(nProps, key)) {
      return true;
    }
    if (!is(props[key], nProps[key])) {
      return true;
    }
  }
  for (let i = 0, l = stateKeys.length; i < l; i += 1) {
    const key = stateKeys[i];
    if (!Object.prototype.hasOwnProperty.call(nState, key)) {
      return true;
    }
    if (!is(state[key], nState[key])) {
      return true;
    }
  }
  return false;
}

/**
 * Enhances a component with change checks and console logs powered by debugjs
 * @public
 * @example
 * // Usage as a high order component
 * class MyComponent extends React.Component {
 *  render() { return <p>Hello World!</p>; }
 * }
 * withDebugInfo(MyComponent); // Your enhanced component
 * @param {React.Component} Component - the component to be enhanced
 * @return {React.Component} - the enhanced component
 * @since 2.0.0
 */
export function withDebugInfo(Component) {
  let debug;
  return class extends React.Component {
    static displayName = getComponentName(Component);

    static propTypes = Component.propTypes;

    shouldComponentUpdate(nextProps, nextState) {
      debug = debug || require('./debug');
      debug.reportChanges(this, nextProps, nextState);
      return shouldUpdate(this, nextProps, nextState);
    }

    render() {
      return <Component {...this.props} />;
    }
  };
}

/**
 * Encapsulates the shouldUpdate logic as a high order function
 * @public
 * @example
 * // Usage as a high order component
 * class MyComponent extends React.Component {
 *  render() { return <p>Hello World!</p>; }
 * }
 * withPureRender(MyComponent); // Your enhanced component
 * @param {React.Component} PureComponent - the component to be enhanced
 * @return {React.Component} - the enhanced component
 * @since 1.0.0
 */
export function withPureRender(PureComponent) {
  return class extends React.Component {

    static displayName = getComponentName(PureComponent);

    static propTypes = PureComponent.propTypes;

    shouldComponentUpdate(nextProps, nextState) {
      return shouldUpdate(this, nextProps, nextState);
    }

    render() {
      return <PureComponent {...this.props} />;
    }
  };
}