React-Redux 中的严格相等 (===) 与浅相等检查

She*_*ueh 16 javascript comparison equality reactjs react-redux

我正在研究React-Redux-v.7.1提供的 Hooks API 如何工作,并在平等比较和更新https://react-redux.js.org/api/hooks#equality-comparisons-and-更新):

"从 v7.1.0-alpha.5 开始,默认比较是严格的===引用比较。这与connect()不同,后者使用对mapState调用结果的浅层相等性检查来确定是否需要重新渲染. 这对您应该如何使用useSelector()有几个影响。

我想知道为什么严格相等比connect()使用的浅相等更好?然后我查看了他们的平等比较:

useSelector()的默认严格相等检查只是检查a===b

const refEquality = (a, b) => a === b
Run Code Online (Sandbox Code Playgroud)

并且react-redux/src/connect/connect.js 中的相等性检查使用Object.is()和其他检查,这与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
}
Run Code Online (Sandbox Code Playgroud)

根据MDN 中 Object.is() 的描述:Object.is没有类型转换,也没有对 NaN、-0 和 +0 进行特殊处理(除了那些特殊的数值外,的行为与 === 相同)

我不知道为什么a === b比一系列相等检查更好。(这是我第一次在这里提问,为粗鲁或缺乏信息而道歉)

azu*_*ndo 20

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);
Run Code Online (Sandbox Code Playgroud)

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);
  ...
}
Run Code Online (Sandbox Code Playgroud)

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