How React checks if 2 values are the same value
Tags: react, javascript, beginners, webdev
Published: 2022-02-14 05:47:53

---
canonical_url: https://josemukorivo.com/blog/how-react-checks-if-2-values-are-the-same-value-3g4a
---

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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) `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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is#examples)

```js
// 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](https://github.com/facebook/react/blob/main/packages/shared/objectIs.js) and go through it.

```js
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.
```js
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.
```js
(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](https://github.com/facebook/react/blob/main/packages/shared/objectIs.js) for what we went through.
```js
/**
 * 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](https://github.com/facebook/react/blob/main/packages/shared/shallowEqual.js) and go through it. Also note our `objectIs` function is imported as just `is`.

```js
/**
 * 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.