В этой статье вы получите представление о том, как работает компонент React, и о методе сравнения, который он использует, чтобы определить, когда следует повторно отображать пользовательский интерфейс.

Простой пример, который мы используем: нажатие кнопки приведет к повторному рендерингу компонента, и вы увидите журнал в консоли.

const [state, setState] = useState()

// to track how many time the component renders
console.log('render at ' + new Date().toLocaleTimeString())

return (
  <Button onClick={() => setState('something')} />
)

Поэтому большинство людей думают, что вызов setState всегда будет вызывать повторную визуализацию компонента.

Удивительно, но в некоторых случаях это не так. На самом деле компонент React выполняет поверхностное сравнение, чтобы проверить, изменился ли state перед фактическим рендерингом.

Прежде всего, давайте посмотрим, как JavaScript выполняет строгое сравнение (тройное равенство):

true === true  // true
false === false  // true

0 === 0  // true
Infinity === Infinity  // true

"string" === "string"  // true

null === null  // true
undefined === undefined  // true

NaN === NaN // false

[] === []  // false
[1, "string"] === [1, "string"]  // false

{} === {}  // false
{id: 9} === {id : 9}  // false

Поверхностное сравнение аналогично строгому сравнению при сравнениипримитивных типов данных, объектов и массивов. Единственным исключением является NaN

На самом деле ReactJS использует Object.is для сравнения состояния и свойств.

NaN === NaN // false

Object.is(NaN, NaN)  // true

Пример с примитивными типами данных

const [name, setName] = useState("Jenny")

// to track how many time the component renders
console.log('render at ' + new Date().toLocaleTimeString())

return (
  <>
    <Button onClick={() => setName("Jenny")} />
    <Button onClick={() => setName("Emily")} />
  </>
)
  • нажатие на первую кнопку не вызовет повторную визуализацию компонента.
  • щелчок по второй кнопке запустит повторную визуализацию компонента.
Object.is("Jenny", "Jenny")  // true

Object.is("Jenny", "Emily")  // false

Пример с типами объектов

const [user, setUser] = useState({name: "Jenny"})

// to track how many time the component renders
console.log('render at ' + new Date().toLocaleTimeString())

return (
   <Button onClick={() => {
      const newUser = {name: "Jenny"}
      setUser(newUser)
   }/>
)

Нажатие на кнопку вызовет повторную визуализацию компонента, даже если свойство name из newUser имеет то же значение, что и старое состояние.

Object.is("Jenny", "Jenny")  // true

Object.is({name: "Jenny"}, {name: "Jenny"})  // false

Ключевые выводы

ReactJS использует поверхностное сравнение вместо глубокого сравнения по соображениям производительности.

Поверхностное сравнение — это быстрый способ определить, различаются ли два объекта, сравнивая только их ссылки или примитивные значения, а не рекурсивно сравнивая их вложенные свойства. Это означает, что если два объекта имеют одинаковые свойства и значения, но не являются одним и тем же объектом, они не будут считаться равными при поверхностном сравнении.

С другой стороны, глубокое сравнение включает рекурсивное сравнение всех свойств объектов, что может занять много времени и ресурсов для больших или сложных объектов.