Leniwe ładowanie komponentów w React dzięki import

Ten wpis jest 31 częścią z 39 w kursie React.js
wp-content/uploads/2017/10/React_logo_wordmark-300x101-1-e1508612391308.png
  1. Wprowadzenie do kursu React.js od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami

Czy w React da się ładować komponenty leniwie? A więc masz już sporą aplikację. Chcesz ją jakoś zoptymalizować. Do głowy przychodzi Ci, oczywiście, podział jej na kilka części, które będą ładowane na żądanie, dopiero gdy będą potrzebne — tzw. lazy load. Z pomocą przychodzi operator import(…) — po prostu!

Operator import(…)

Składni import something from 'something' używasz na pewno codziennie. W skrócie, powoduje ona, że dana zależność ładowana jest zanim reszta kodu zostanie uruchomiona. W praktyce, jeśli używasz webpacka, cały kod łączony jest w jeden plik (lub kilka) i moduły ładują się synchronicznie.

Ale import to także operator, który można użyć w sposób podobny do funkcji: import(…). Służy on do dynamicznego ładowania modułów na żądanie. Leniwie. I zwraca Promise. Brzmi dobrze?

Leniwy komponent React

A więc zastanów się jak można by użyć tego operatora do leniwego ładowania komponentów w React:

  • Komponent musi się ładować na żądanie, dopiero gdy będzie miał być wyświetlony.
  • Mogę stworzyć komponent (np. AsyncComponent), który jako prop przyjmie funkcję ładującą komponent, i wyświetli gdy będzie taka potrzeba.
  • Użyję do tego metody componentDidMount oraz setState i operatora import(…)

Tak mniej-więcej wyglądał mój proces myślowy 😉 Do dzieła!

Asynchroniczny komponent

Robię dokładnie to, co napisałem. Tworzę komponent, który po wyświetleniu wywoła przekazaną funkcję (która załaduje komponent) i go wyświetli, gdy już będzie gotowy:

export class AsyncComponent extends React.Component {
  state = { Component: null, isLoading: false }; // (1)

  componentDidMount() {
    if (!this.state.Component && !this.state.isLoading) { // (2)
      this.setState({ isLoading: true }, this.loadComponent); // (3)
    }
  }
}

Początkowy stan (1) — komponentu nie ma, ładowanie się jeszcze nie rozpoczęło. Następnie w componentDidMount, o ile jeszcze nie ma komponentu i nie jest on właśnie ładowany (2), ustawiam isLoading na true i ładuję komponent. Jak wygląda this.loadComponent?

loadComponent = () => {
  this.props.componentProvider().then(Component => { // (4)
    this.setState({ Component, isLoading: false }); // (5)
  });
};

Wywołuję przekazaną funkcję (4), a gdy komponent się załaduje to ustawiam go w state (5). Pozostaje tylko render:

render() {
  const { Component } = this.state;
  if (Component) {
    return <Component />;
  }
  return null;
}

I już!

Operator import(…)

Nowego komponentu używam w taki sposób, z operatorem import():

<AsyncComponent
  componentProvider={() =>
    import("./AnotherComponent").then(module => module.AnotherComponent) /* (6) */
  }
/>

Tutaj tkwi cała magia (6). Operator import(…) załaduje moduł przekazany jako argument i zwróci Promise z tym modułem!

Dodatkowe propsy

Okej, a co jeśli chcesz przekazać do AnotherComponent jakieś propsy? To prostsze niż się wydaje! Dodaj do AsyncComponent nowy props, np. o nazwie componentProps (7) i użyj operator spread (8):

const componentProps = { props: 'lalala', anotherValue: 123 };
<AsyncComponent
  componentProps={componentProps} /* (7) */
  componentProvider={() =>
    import("./AnotherComponent").then(module => module.AnotherComponent)
  }
/>

A w samym AsyncComponent zmieniam tylko metodę render:

render() {
  const { Component } = this.state;
  if (Component) {
    return <Component {...this.props.componentProps} />; /* (8) */
  }
  return null;
}

Efekt

Jeśli teraz zajrzysz do zakładki Network to zobaczysz tam dodatkowe żądanie — np. o nazwie 1.chunk.js. To właśnie to!

Lazy load komponentów React

Cały kod znajdziesz na GitHubie: github.com/mmiszy/typeofweb-kurs-react/tree/contacts-list-5-async

Podsumowanie

Tajemna wiedza, którą Ci tutaj przekazałem przyda Ci się w bardziej rozbudowanych aplikacjach. Wyobraź sobie na przykład, że Twoje komponenty są leniwie doładowywane gdy zmienia się adres strony — pobierane jest to, co akurat się przyda. Brzmi jak marzenie? 😉 Naucz się React z naszymi szkoleniami!

Jeśli chcesz na bieżąco dowiadywać się o kolejnych częściach kursu React.js to koniecznie śledź mnie na Facebooku i zapisz się na newsletter.

Ćwiczenie

Ćwiczenie: Zrefaktoruj App.jsx z poprzedniej części tak, aby komponent ContactsList był ładowany tylko jeśli kontakty są załadowane i jest więcej niż 0 (this.props.contacts).

Nawigacja po kursie:
  1. Wprowadzenie do kursu React.js od podstaw
  2. Poznaj React.js
  3. Pierwszy komponent w React.js
  4. Props czyli atrybuty w React.js
  5. Podział na komponenty w React.js
  6. Klasy jako komponenty React.js
  7. Interakcja z komponentami React.js
  8. Stan komponentów React.js
  9. State w React.js 2
  10. Metody cyklu życia komponentu w React.js
  11. React.js w przykładach: filtrowanie statycznej listy
  12. Tworzenie aplikacji React.js dzięki create-react-app
  13. React.js na GitHub Pages dzięki create-react-app
  14. Testowanie aplikacji React.js — podstawy Enzyme
  15. Testowanie React.js w Enzyme — props, state i interakcje
  16. Poprawne bindowanie funkcji w React.js
  17. Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
  18. Komunikacja pomiędzy komponentami w React.js
  19. Komunikacja z API w React.js
  20. Formularze w React.js — kontrolowane komponenty
  21. Formularze w React.js — niekontrolowane komponenty
  22. Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
  23. TDD w React.js z pomocą react-testing-library
  24. Flux i Redux
  25. React + Redux — kurs: wprowadzenie i podstawy
  26. React + Redux — filtrowanie listy, proste selektory
  27. Projektowanie komponentów: Presentational & Container Components
  28. Asynchroniczność w Redux: redux-thunk
  29. Kiedy używać state, a kiedy Redux?
  30. Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
  31. Leniwe ładowanie komponentów w React dzięki import
  32. Higher Order Reducers — Redux i powtarzanie kodu
  33. React Hooks — wprowadzenie i motywacja
  34. React Hooks: useState, czyli stan w komponentach funkcyjnych
  35. React Hooks: useState — wiele stanów, callbacki i inne niuanse
  36. React Hooks: useEffect — efekty uboczne w komponencie
  37. React Hooks a żądania do API
  38. useReducer — przenoszenie logiki poza komponent
  39. useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami