- React.js: Wprowadzenie do kursu od podstaw
- Poznaj React.js
- Pierwszy komponent w React.js
- Props czyli atrybuty w React.js
- Podział na komponenty w React.js
- Klasy jako komponenty React.js
- Interakcja z komponentami React.js
- Stan komponentów React.js
- State w React.js 2
- Metody cyklu życia komponentu w React.js
- React.js w przykładach: filtrowanie statycznej listy
- Tworzenie aplikacji React.js dzięki create-react-app
- React.js na GitHub Pages dzięki create-react-app
- Testowanie aplikacji React.js — podstawy Enzyme
- Testowanie React.js w Enzyme — props, state i interakcje
- Poprawne bindowanie funkcji w React.js
- Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
- Komunikacja pomiędzy komponentami w React.js
- Komunikacja z API w React.js
- Formularze w React.js — kontrolowane komponenty
- Formularze w React.js — niekontrolowane komponenty
- Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
- TDD w React.js z pomocą react-testing-library
- Flux i Redux: globalny store i jednokierunkowy przepływ danych
- React + Redux — kurs: wprowadzenie i podstawy
- React + Redux — filtrowanie listy, proste selektory
- Projektowanie komponentów: Presentational & Container Components
- Asynchroniczność w Redux: redux-thunk
- Kiedy używać state, a kiedy Redux?
- Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
- Leniwe ładowanie komponentów w React dzięki import
- Higher Order Reducers — Redux i powtarzanie kodu
- React Hooks — wprowadzenie i motywacja
- React Hooks: useState, czyli stan w komponentach funkcyjnych
- React Hooks: useState — wiele stanów, callbacki i inne niuanse
- React Hooks: useEffect — efekty uboczne w komponencie
- React Hooks a żądania do API
- useReducer — przenoszenie logiki poza komponent
- useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami
- Wady React Hooks
- React Hooks: Piszemy własne hooki!
React Hooks mocno upraszczają właściwie wszystko, co do tej pory robiłaś. W jednym z pierwszych odcinków kursu pokazywałem, jak można w szybki sposób pobierać dane z API używając fetch
w Reakcie. Czy Hooki coś tutaj zmieniają? Ależ tak!
Fetch do tej pory
Zaczniemy może od przyjrzenia się, jak taki fetch wyglądał do tej pory w klasach:
export class App extends React.Component {
state = {
contacts: []
};
componentDidMount() {
fetch("https://randomuser.me/api/?format=json&results=10")
.then(res => res.json())
.then(json => this.setState({ contacts: json.results }));
}
render() {
return <ContactsList contacts={this.state.contacts} />;
}
}
Co tu się dokładnie dzieje? Najpierw deklaruję state, następnie w componentDidMount
wykonuję zapytanie do API, czekam na wynik i rezultat zapisuję do stanu. Ten kod pochodzi ze starszego wpisu, tam też znajdziesz więcej wyjaśnień:
Nie wygląda skomplikowanie, ale czy może być jeszcze prostsze?
Wchodzą Hooki
Z użyciem znanych Ci już Hooków useState
i useEffect
wygląda to tak:
function App() {
const [contacts, setContacts] = useState([]);
useEffect(() => {
fetch("https://randomuser.me/api/?format=json&results=10")
.then(res => res.json())
.then(json => setContacts(json.results));
}, []);
return <ContactsList contacts={contacts} />
}
Zwróć uwagę na jedną ważną rzecz: Jako drugi argument do useEffect
podałem tutaj pustą tablicę. Po co? Aby fetch
nie wykonywał się przy każdym renderze, a tylko za pierwszym razem!
No ładnie, prawda? Ale wcale nie jest dużo krótsze. Dodajmy więc nowe wymaganie…
Aktualizacja wyników gdy zmienia się ID
Załóżmy, że Twój komponent ma wyświetlać dane pobrane z API dla danego ID przekazanego mu jako props. W użyciu:
<App id={…} />
Gdy id
się zmieni, komponent ma pobrać dane na nowo i wyświetlić. Jak to wygląda w klasie?
export class App extends React.Component {
state = {
contacts: []
};
fetchData() {
fetch(`https://randomuser.me/api/?format=json&results=10&seed=${this.props.id}`)
.then(res => res.json())
.then(json => this.setState({ contacts: json.results }));
}
componentDidMount() {
this.fetchData();
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.fetchData();
}
}
render() {
return <ContactsList contacts={this.state.contacts} />;
}
}
Łołoło, trochę nam ten komponent urósł 🤔 A z hookami?
function App({ id }) {
const [contacts, setContacts] = useState([]);
useEffect(() => {
fetch(`https://randomuser.me/api/?format=json&results=10&seed=${id}`)
.then(res => res.json())
.then(json => setContacts(json.results));
}, [id]);
return <ContactsList contacts={contacts} />
}
To jest nadal tak samo krótkie, jak poprzednio. Zmieniły się tylko 3 fragmenty kodu: Props jako argument do komponentu, fetch pod inny adres i tablica jako drugi argument do useEffect
już nie jest pusta: [id]
.
W tym kodzie jest bug
Jest subtelny i pewnie się na niego nie natkniesz, ale uwierz mi, że w tym kodzie jest bug. Gdy szybko zmienisz wartość ID, wykonają się w krótkim czasie dwa żądania do API. A co się stanie, jeśli odpowiedź na pierwsze żądanie przyjdzie później niż na drugie? Komponent wyświetli nieaktualne dane. Bug!
Aby go naprawić, trzeba anulować poprzednie żądanie. Jak to wygląda w klasie?
export class App extends React.Component {
state = {
contacts: []
};
controller = new AbortController();
fetchData() {
this.controller.abort();
fetch(`https://randomuser.me/api/?format=json&results=10&seed=${this.props.id}`, { signal: this.controller.signal })
.then(res => res.json())
.then(json => this.setState({ contacts: json.results }));
}
componentDidMount() {
this.fetchData();
}
componentWillUnmount() {
this.controller.abort();
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.fetchData();
}
}
render() {
return <ContactsList contacts={this.state.contacts} />;
}
}
Używam tutaj eksperymentalnego AbortController
, więc przestudiuj ten kod dokładnie 🙂 Aby anulować żądanie, wywołuję controller.abort()
— w klasie musiałem sobie zapisać instancję AbortController
.
Dodatkowo dopisałem też brakujące componentWillUnmount
, aby komponent nie pobierał niepotrzebnie danych, gdy już zostanie odmontowany.
Z Hookami jest dużo prościej, bo korzystasz z naturalnego dla JS domknięcia:
function App({ id }) {
const [contacts, setContacts] = useState([]);
useEffect(() => {
const controller = new AbortController();
fetch(`https://randomuser.me/api/?format=json&results=10&seed=${id}`, { signal: controller.signal })
.then(res => res.json())
.then(json => setContacts(json.results));
return () => controller.abort();
}, [id]);
return <ContactsList contacts={contacts} />
}
Tak, to już!
Hooki są czystsze
Zwróć uwagę jak bardo porozrzucany jest kod w ostatnim przykładzie w klasie — constructor
, componentDidMount
, componentWillUnmount
i componentDidUpdate
. W hookach zaś, wszystko jest obok siebie. Powiązany kod leży blisko.
Dodatkowo, łatwo mogłabyś się pokusić o stworzenie nowego hooka, który by całą tę logikę enkapsulował. Dzięki temu mogłabyś łatwo go używać w różnych miejscach, bez konieczności duplikowania kodu. To w praktyce niemożliwe przy użyciu klas (chyba, że przez HoC lub zagnieżdżając kolejne komponenty…)
Pytania?
Naucz się React z naszymi szkoleniami! Jeśli chcesz na bieżąco śledzić kolejne części kursu React.js to koniecznie polub mnie na Facebooku i zapisz się na newsletter.
- React.js: Wprowadzenie do kursu od podstaw
- Poznaj React.js
- Pierwszy komponent w React.js
- Props czyli atrybuty w React.js
- Podział na komponenty w React.js
- Klasy jako komponenty React.js
- Interakcja z komponentami React.js
- Stan komponentów React.js
- State w React.js 2
- Metody cyklu życia komponentu w React.js
- React.js w przykładach: filtrowanie statycznej listy
- Tworzenie aplikacji React.js dzięki create-react-app
- React.js na GitHub Pages dzięki create-react-app
- Testowanie aplikacji React.js — podstawy Enzyme
- Testowanie React.js w Enzyme — props, state i interakcje
- Poprawne bindowanie funkcji w React.js
- Odpowiadam na pytania: Babel, ECMAScript, destrukturyzacja, onClick, className
- Komunikacja pomiędzy komponentami w React.js
- Komunikacja z API w React.js
- Formularze w React.js — kontrolowane komponenty
- Formularze w React.js — niekontrolowane komponenty
- Odpowiadam na pytania: props, nawiasy klamrowe, funkcje vs klasy, import react
- TDD w React.js z pomocą react-testing-library
- Flux i Redux: globalny store i jednokierunkowy przepływ danych
- React + Redux — kurs: wprowadzenie i podstawy
- React + Redux — filtrowanie listy, proste selektory
- Projektowanie komponentów: Presentational & Container Components
- Asynchroniczność w Redux: redux-thunk
- Kiedy używać state, a kiedy Redux?
- Nowe metody cyklu życia: getDerivedStateFromProps i getSnapshotBeforeUpdate
- Leniwe ładowanie komponentów w React dzięki import
- Higher Order Reducers — Redux i powtarzanie kodu
- React Hooks — wprowadzenie i motywacja
- React Hooks: useState, czyli stan w komponentach funkcyjnych
- React Hooks: useState — wiele stanów, callbacki i inne niuanse
- React Hooks: useEffect — efekty uboczne w komponencie
- React Hooks a żądania do API
- useReducer — przenoszenie logiki poza komponent
- useMemo, useCallback, czyli rozwiązanie problemów ze zmieniającymi się propsami
- Wady React Hooks
- React Hooks: Piszemy własne hooki!
Fajny tekst. Mam jednak sugestię dotyczącą widoku artykułu z listą poprzednich wpisów. Przydałby się jakiś przycisk w stylu przejdź do artykułu, bo scrollowanie się przez cały spis treści zaczyna być nużące 😉
Cześć,
Czy mogę w useEffect() zapisać dane z api do tablicy i użyć w innym hooku jako initial data?
Jeśli tak to jak to zrobić?
A co ma się dziać zanim dane przyjdą z API?
Rozumiem ze autor jest wielkim pasjonatem hookow ale takie na sile szukanie argumentow , j.np. enkapsulacja jest obecna po stronie klas, co z reszta sam autor przyznaje sam sobie zaprzeczajac. Fajny kurs ^^
Lepszą przenośność i enkapsulację logiki zapewniają hooki.
[…] React Hooks a żądania do API […]