This content originally appeared on DEV Community and was authored by Joseph Mukorivo
If you have ever wondered how react internally checks if two values are the same value this is the right blog for you. Well before we even start looking at how React implements that lets first check to see if we already have a function in plain javascript that does that.
We already have this function Object.is()
for that, and according to MDN Web docs Object.is()
determines whether two values are the same value. Two values are the same if one of the following holds:
- both undefined
- both null
- both true or both false
- both strings of the same length with the same characters in the same order
- both the same object (meaning both values reference the same object in memory)
- both numbers and
- both +0
- both -0
- both NaN
- or both non-zero and both not NaN and both have the same value
Note that Object.is()
is not the same as the ==
operator as it does not do any type coercion if the types of values are not the same. It's also not the same as the ===
operator because they treat NaNs
and signed zeros differently. With the ===
or the ==
operators -0
and +0
are treated as equal. The ===
operator also treat NaN
and Number.NaN
as not equal.
Example usage from MDN Web docs
// Case 1: Evaluation result is the same as using ===
Object.is(25, 25); // true
Object.is('foo', 'foo'); // true
Object.is('foo', 'bar'); // false
Object.is(null, null); // true
Object.is(undefined, undefined); // true
Object.is(window, window); // true
Object.is([], []); // false
var foo = { a: 1 };
var bar = { a: 1 };
Object.is(foo, foo); // true
Object.is(foo, bar); // false
// Case 2: Signed zero
Object.is(0, -0); // false
Object.is(+0, -0); // false
Object.is(-0, -0); // true
Object.is(0n, -0n); // true
// Case 3: NaN
Object.is(NaN, 0/0); // true
Object.is(NaN, Number.NaN) // true
So this is how Object.is()
works, but since we don't live in a perfect world React
can't just use this implementation, they need to have some sort of a polyfill for environments like old browsers that don't support Object.is()
. Let paste the polyfill code from React source code and go through it.
function is(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
);
}
This function is inline, but to make sure that everyone understands let me put it in a long format.
function is(x: any, y: any) {
// SameValue algorithm
if (x === y) {
// return true if x and y are not 0, OR
// if x and y are both 0 of the same sign.
return x !== 0 || 1 / x === 1 / y;
} else {
// return true if both x AND y evaluate to NaN.
// The only possibility for a variable to not be strictly equal to itself
// is when that variable evaluates to NaN (example: Number.NaN, 0/0, NaN).
return x !== x && y !== y;
}
}
The if
condition is hit if the values we pass are equal for example if we pass 2
and 2
the condition immediately returns with true because 2
is not equal to 0
.
If we pass +0
and -0
the OR part of the return statement is evaluated and 1/0
which is equal to Infinity
will be compared to 1/-0
which is -Infinity
which will then evaluate to false. This makes sure that even if we pass zeros
they are of the same sign.
All that can be refactored to what we have below. If this looks cryptic to you let me know in the comments.
(x === y && (x !== 0 || 1 / x === 1 / y))
For the else part of our function there is a possibility for the function to return true
, that is if both x
AND y
evaluate to NaN
, otherwise return false
.
Below is the whole React source file for what we went through.
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x: any, y: any) {
return (
(x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
);
}
const objectIs: (x: any, y: any) => boolean =
typeof Object.is === 'function' ? Object.is : is;
export default objectIs;
Now the question you may have is where are we going to use this? Well there is another function that uses this method in React called shallowEqual
. The result from this function is what causes React
to rerender. Let's say you pass a name
prop to a component with value=Jose and then later on change the value to Joseph React compares these values using the shallowEqual
function and if it returns false, react rerenders.
I am going to paste the code for this function as it is from React source code and go through it. Also note our objectIs
function is imported as just is
.
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import is from './objectIs';
import hasOwnProperty from './hasOwnProperty';
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA: mixed, objB: mixed): boolean {
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;
}
// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
const currentKey = keysA[i];
if (
!hasOwnProperty.call(objB, currentKey) ||
!is(objA[currentKey], objB[currentKey])
) {
return false;
}
}
return true;
}
export default shallowEqual;
This function accepts 2 arguments and if we pass them to objectIs
and they return true
, the function immediately returns with true
. This is covered by the first if
statement.
If any of the objects we pass is null
, or its type is not equal to object
the function returns with false
. That's covered by the second if
statement.
If the objects we pass have different number of properties, we also return false
that means they are not equal. That's covered by the third if
statement which checks the length of keys to see if they are not equal.
Now the last part is to check if the keys are the same for both objects. We use the Object.hasOwnProperty
for that for each key. objA
and objB
may have the same number of keys and Object.hasOwnProperty
makes sure that even if the number of keys is the same the keys also have the same names.
Please Note this is a shallow comparison so we don't compare the values in the objects so if you mutate an object and pass it React will not be able to see that change.
Thanks for reading, stay tuned for more articles about these small react functions. Follow me if you want to keep looking into the source code for React.
This content originally appeared on DEV Community and was authored by Joseph Mukorivo
Joseph Mukorivo | Sciencx (2022-02-14T05:47:53+00:00) How React checks if 2 values are the same value. Retrieved from https://www.scien.cx/2022/02/14/how-react-checks-if-2-values-are-the-same-value/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.