Source: app/util/assignIfChanged.js

import isEqual from 'lodash/isEqual';

/**
 * @module util/assignIfChanged
 */

/**
 * Merges the properties of `newProps` into `target`, like Object.assign. However, each property
 * is compared with the existing property on the `target` object. If the properties are equal, the
 * property on the `target` object will not be overwritten.
 *
 * _note: This logic is applied for each top-level property of `newProps`, but not for deeply
 * nested properties. _
 *
 * This util is useful when we want to cause as little mutations to the target object as possible.
 * For example, in reducers mutations to objects will generally cause attached selectors and React
 * components to update.
 *
 * @param target {Object} The object to update properties on
 * @param newProps {Object} An object containing new properties
 * @param compare {function} The function that will be used to compare properties from newProps
 * to existing properties on target. Defaults to the
 * {@link https://lodash.com/docs/4.17.4#isEqual|lodash isEqual} util.
 * @returns {Object} A new object with the changed properties from newProps. If no properties have
 * changed, will return a reference to the original object.
 */
function assignIfChanged(target, newProps, compare = isEqual) {
  const result = { ...target };
  let hasChange = false;

  Object.keys(newProps).forEach(newKey => {
    if (!compare(result[newKey], newProps[newKey])) {
      hasChange = true;
      result[newKey] = newProps[newKey];
    }
  });

  return hasChange ? result : target;
}

export default assignIfChanged;