poniedziałek, 19 października 2020

react i referencja

Cykl życia komponentu reacta obejmuje update komponentu, gdy dane na których pracuje komp[onent się zmienią. Dotyczy to zarówno danych przychodzacych z zewnątrz (props) jak i tych tworzonych i uaktualnianych wewnątrz komponentu (state). Jeśli chodzi o dane prymitywne (liczby, ciągi tekstowe, typy logiczne, null, undefined i Symbol) to sprawa jest dość prosta. Wiadomo, że:
console.log(1 === 1); // true
console.log('a' === 'a'); // true
console.log(true === true); // true
console.log(undefined === undefined); // true
console.log(Symbol('a').toString() === Symbol('a').toString()); // true

Czyli jeśli wartość props lub state zmieni się z 1 na 2 to komponent wie, że ma się zrerenderować. Proste.

A jednak, nie do końca. Wszystko co nie jest prymitywem w JS (jest to pewne uproszczenie, będzie osobny wpis na ten temat) jest obiektem. A obiekty nie są przekazywane przez wartość a przez referencję. Innymi słowy JS umieszcza utworzony obiekt gdzieś w pamięci i posługuje się adresem tego obiektu a nie nim samym. Każde utworzenie obiektu tworzy nową referencję.
const a = { id: 1 };
const b = { id: 1 };

console.log(a === a); // true
console.log(a === b); // false
console.log(Object.is(a, a)); // true console.log(Object.is(a, b)); // false

Czyli nawet dwa identyczne obiekty mają dwie różne referencje, więc nie da się ich łatwo porównać. React do porównywania obiektów używa Object.is().

Dlatego, gdy zmieniamy jakąś wartość w obiekcie, powinniśmy stworzyć nową referencję, żeby react zauważył zmianę, na przykład tak:
const [formData, setFormData] = useState({});

function handleChange(evt) {
  const id = evt.target.id;
  const value = evt.target.value;

  const formCopy = { ...formData };
  formCopy[id] = value;
  setFormData(formCopy);
}

Alternatywnym sposobem może być używanie biblioteki jak immutable.js aby każda zmiana w obiekcie zwracała nową referencję.

Brak komentarzy:

Prześlij komentarz