Strict Equality (===) versus Shallow Equality Checks in React-Redux

白昼怎懂夜的黑 提交于 2020-05-23 08:48:45

问题


I'm studying how Hooks APIs provided by React-Redux-v.7.1 works, and saw it mentioned in Equality Comparisons and Updates (https://react-redux.js.org/api/hooks#equality-comparisons-and-updates) that:

"As of v7.1.0-alpha.5, the default comparison is a strict === reference comparison. This is different than connect(), which uses shallow equality checks on the results of mapState calls to determine if re-rendering is needed. This has several implications on how you should use useSelector()."

I'm wondering why strict equality is better than shallow equality that used by connect()? Then I looked into their equality comparisons:

The default strict equality check of useSelector() is simply checking a===b

const refEquality = (a, b) => a === b

And the equality checks in react-redux/src/connect/connect.js is using Object.is() and also other checks, which is same as that in react.

// The polyfill of Object.is()
function is(x, y) {
  if (x === y) {
    return x !== 0 || y !== 0 || 1 / x === 1 / y
  } else {
    return x !== x && y !== y
  }
}

export default function shallowEqual(objA, objB) {
  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

  for (let i = 0; i < keysA.length; i++) {
    if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
      return false
    }
  }

  return true
}

According to the description of Object.is() in MDN: "Object.is does no type conversion and no special handling for NaN, -0, and +0 (giving it the same behavior as === except on those special numeric values)."

I have no idea why a === b is better than a series of equality checks. (It's my first time asking questions here, apologize for rudeness or lack of information)


回答1:


With connect, mapStateToProps returns a composite object of all of the state being selected from the store, so a shallow comparison across its keys makes sense. With useSelector, the pattern is often to only return a single value for each invocation of useSelector, similar to the way that the useState hook only handles a single value instead of all of the state values. So if each call to useSelector returns a value directly then a strict equality check makes sense vs a shallow comparison. A short example should make this more clear.

import {connect} from 'react-redux';

const mapStateToProps = state => (
  {keyA: state.reducerA.keyA, keyB: state.reducerB.keyB}
);
export default connect(mapStateToProps)(MyComponent);

Here mapStateToProps is called every time the store changes in any way, so the return value will always be a new object, even if keyA and keyB do not change. So a shallow comparison is used to decide if a re-render is needed.

For the hooks case:

import {useSelector} from 'react-redux';

function MyComponent(props) {
  const keyA = useSelector(state => state.reducerA.keyA);
  const keyB = useSelector(sate => state.reducerB.keyB);
  ...
}

Now the result of the useSelector hooks are the individual values from the store, not a composite object. So using strict equality as the default here makes sense.

If you want to use only a single useSelector hook that returns a composite object, the docs you linked to have an example of using the shallowEqual equality function: https://react-redux.js.org/api/hooks#equality-comparisons-and-updates



来源:https://stackoverflow.com/questions/58212159/strict-equality-versus-shallow-equality-checks-in-react-redux

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!