问题
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