W poprzedniej lekcji omówiliśmy sobie, czym jest mount render (tzw. initial render) oraz diff render jak ja go nazywam (tzw. re-render). Dzisiaj omówimy sobie, co się dzieje przy dosłownie każdym renderze.

Przypomnijmy:

  • mount render (initial render) – render wykonany po componentDidMount (React porzucił już OOP, to był zdaje się odpowiednik swego rodzaju connectedCallback z web components)
  • diff-render:
    • Gdy zmieni się stan komponentu
    • Gdy zmieni się stan komponentu rodzica albo rodzic ma diff-render
    • Gdy zmieni się kontekst
  • każdy render = mount-render + diff-rendery

Ok, academind ma na swoim githubie taki oto kod:

import { useState, useCallback } from 'react';

import QUESTIONS from '../questions.js';
import Question from './Question.jsx';
import Summary from './Summary.jsx';

export default function Quiz() {
  const [userAnswers, setUserAnswers] = useState([]);

  const activeQuestionIndex = userAnswers.length;
  const quizIsComplete = activeQuestionIndex === QUESTIONS.length;

  const handleSelectAnswer = useCallback(function handleSelectAnswer(
    selectedAnswer
  ) {
    setUserAnswers((prevUserAnswers) => {
      return [...prevUserAnswers, selectedAnswer];
    });
  },
  []);

  const handleSkipAnswer = useCallback(
    () => handleSelectAnswer(null),
    [handleSelectAnswer]
  );

  if (quizIsComplete) {
    return <Summary userAnswers={userAnswers} />
  }

  return (
    <div id="quiz">
      <Question
        key={activeQuestionIndex}
        index={activeQuestionIndex}
        onSelectAnswer={handleSelectAnswer}
        onSkipAnswer={handleSkipAnswer}
      />
    </div>
  );
}

Na razie wszystkiego nie ogarniemy, ale chciałby zaznaczyć kilka kwestii:

  • Mount render i każdy diff-render będzie wywoływał ponowne przypisanie wartości do nowych w zasadzie stałych
  • Gdyby ktoś chciał aby to wszystko nie było wyliczane od nowa przy każdym renderze, musiałby:
    • albo wynieść te stałe powyżej poziomu RFC (nie w sensie „powyżej” Reacta tylko powyżej kodu JS, nad RFC, ale wtedy one nie mogłyby się nigdy zmieniać)
    • albo użyć useMemo i wtedy te stałe będą keszowane i ponownie obliczane tylko wtedy, gdy zmienią się zależności
  • Mamy też definicje funkcji, załóżmy, że useCallback nie ma
  • Każda definicja funkcji wewnątrz RFC będzie tworzona od nowa, do zera, gdy nastąpi jakikolwiek render (mount render lub diff rendery)
  • Jeżeli ktoś chciałby aby definicja funkcji nie była tworzona od zera, musiałby:
    • Wynieść ją powyżej poziomu RFC (powyżej w sensie kodu JS, nie struktury komponentów React)
    • Wynieść ją do innego pliku i zaimportować
    • Jeżeli ma się zmieniać, ale tylko wtedy, gdy to konieczne, to używa się do tego useCallback z tablicą zależności

I mamy użycie useCallback:

  • Pierwsza funkcja to handler, który ma pustą tablicę zależności, bo ta funkcja nie zmienia się nigdy – zawsze do setState wpuszcza poprzednie odpowiedzi + nową odpowiedź
  • Druga funkcja polega na tej pierwszej, ale jeśli pierwsza się nie zmieni to druga też nie ulegnie zmianie – ma wpuszczać null jako nową odpowiedź, gdyby było skip answer

Ok, podsumujmy, co już wiemy, bo będzie nam to do następnej lekcji potrzebne:

  • Mount wywołuje mount render
  • Zmiana stanu wywołuje diff-render
  • Zmiana stanu rodzica (diff-render rodzica) wywołuje diff render dziecka
  • Jeżeli RFC ma memo, to diff render rodzica wywoła diff-render dziecka tylko wtedy, gdy przekazane zostaną inne propsy
  • Każdy render wywołuje utworzenie nowych stałych/zmiennych wewnątrz RFC i przypisanie im wartości
  • Jeżeli użyjemy useMemo, to każdy render będzie wywoływał utworzenie nowych stałych/zmiennych tylko wtedy, gdy zmienią się im zależności
  • Każdy render wywołuje utworzenie nowych definicji funkcji wewnątrz RFC
  • Jeżeli nie chcemy tworzyć od nowa definicji funkcji wewnątrz RFC to używamy useCallback i tablicy zależności

Ok, postarajmy się to zrozumieć, w następnych lekcjach temat będzie kontynuowany.