- 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!
Redux! Kurs Reacta dorobił się odcinka o Reduksie! Powoli wprowadzę Cię świat Reduksa, nauczysz się używać tej łatwej biblioteki, poznasz koncepty stojące za nią i napiszesz prostą aplikację.
Redux: Oto wpis, na który wszyscy czekali! 🙂 Nie będzie teorii. Od razu zaczniemy używać Reduksa. Bo on jest tak naprawdę niesamowicie łatwy w obsłudze. Brzmi dobrze? Zaczynajmy!
Układ folderów przedstawiony w tym wpisie jest maksymalnie prosty. O strukturze większej aplikacji będę pisał jeszcze później.
Teoria
Jeśli jednak interesuje Cię teoria stojąca za Flux i Redux znajdziesz ją w innym moim wpisie:
Na pewno warto przeczytać o wzorcach projektowych stojących za Reduksem! Warto też zajrzeć do dokumentacji Redux.
Redux w praktyce
Do aplikacji z listą kontaktów (źródło tutaj: github.com/mmiszy/typeofweb-kurs-react/tree/contacts-list-1) dodasz Reduksa. Kontakty będą zapisywane w storze. Jeśli chcesz sobie odświeżyć wątek to zajrzyj do wpisu na temat łączenia Reacta z API.
Zacznij od zainstalowania paczek redux
i react-redux
:
npm install --save react-redux redux
Praca z Reduksem składa się z 3 kroków:
- Stworzenie store
- Zdefiniowanie akcji
- Napisanie reducerów
To co mnie bardzo przytłaczało na początku pracy z Reduksem to ilość kodu, który musiałem napisać, a który „nic nie robił”. Nie martw się jednak! To tylko pozory. Im bardziej rozbudowana stanie się Twoja aplikacja tym tego kodu jest mniej, a zalety posiadania spójnego stanu są nieocenione.
Store w React
Store przechowuje stan całej Twojej aplikacji. Stwórz plik store.js
, a w nim tylko kilka linii kodu:
import { createStore } from 'redux';
import reducers from './reducers';
export const store = createStore(reducers);
Następnie musisz sprawić, aby React mógł z tego store’a korzystać. Podstawowym sposobem na to jest zmodyfikowanie Twojego kodu w index.js
i użycie komponentu <Provider>
. Do tego komponentu musisz przekazać props store
:
import { store } from "./store";
import { Provider } from "react-redux";
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
Akcje w Redux
Akcje reprezentują zmiany stanu aplikacji. Użycie akcji to jedyny sposób na zmianę czegokolwiek w storze. Potrzebna Ci będzie (na razie) tylko jedna akcja reprezentująca nadejście danych z API. Nazwijmy ją FETCH_CONTACTS_SUCCESS
.
Stwórz folder actions
a w nim plik index.js
. Umieścisz w nim funkcje zwracające akcje dla zadanych argumentów (action creators). Na razie jest tylko jeden action creator:
export const contactsFetched = (contacts) => ({
type: 'FETCH_CONTACTS_SUCCESS',
contacts
});
Reducery w Redux
Reducer to funkcja, która przyjmuje stan aplikacji oraz akcję i na tej podstawie generuje nowy, zaktualizowany stan. Nasz reducer nazwiemy contacts
. A więc w folderze reducers
stwórz plik contacts.js
:
export const contacts = (state = [], action) => { // (1)
switch (action.type) { // (2)
case 'FETCH_CONTACTS_SUCCESS':
return [
...action.contacts
]
default:
return state
}
}
A CO TU SIĘ WYDARZYŁO? Spokojnie, powoli 😉
Reducer to nic innego jak funkcja, która przyjmuje obecny stan i akcję (1). Następnie sprawdza jaka to akcja (2). Jeśli jest to FETCH_CONTACTS_SUCCESS
to oznacza, że mamy nową listę kontaktów i możemy cały obecny stan nią właśnie zastąpić. W każdym innym przypadku — zwracamy stan bez zmian.
combineReducers
Jak teraz powiedzieć reduksowi, aby użył Twojego reducera? Ponieważ reducerów może być wiele w aplikacji (a każdy z nich może operować na wycinku stanu), musisz je jakoś połączyć. Służy do tego funkcja combineReducers
. Do folderu reducers
dodaj plik index.js
:
import { combineReducers } from "redux";
import { contacts } from "./contacts";
export default combineReducers({
contacts
});
Połączenie Redux + React
Uff, to było skomplikowane. Szczęśliwie, to tylko troszkę boilerplate’u, a sama praca z Reduksem dalej jest już bardzo przyjemna!
Co teraz? Podłączamy aplikację do Reduksa! Jak? Używając funkcji connect
.
Plan jest taki: „Udekorujesz” komponent App
dzięki funkcji connect
(jest to HoC, o którym będę pisał później). connect
jako argumenty przyjmuje dwie funkcje zwyczajowo nazywane mapStateToProps
i mapDispatchToProps
mapStateToProps
— jako argument przyjmuje cały stan i musi zwrócić propsy dla danego komponentumapDispatchToProps
— jako argument przyjmuje funkcję lub obiekt z action creatorami
connect
Kilka linii kodu wyraża więcej niż tysiąc słów:
import { connect } from "react-redux";
import { contactsFetched } from "./actions";
const mapStateToProps = (state) => {
return {
contacts: state.contacts // (1)
}
};
const mapDispatchToProps = { contactsFetched }; // (2)
export const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App); // (3)
W mapStateToProps
zwracam interesujący mnie fragment stanu. Pamiętaj, że state
w prawdziwej aplikacji może mieć kilka…dziesiąt własności. Mój komponent potrzebuje tylko contacts
, więc tylko tyle mu przekazuję (1).
Dalej mapDispatchToProps
definiuję jako obiekt z jednym action creatorem (2).
I ostatecznie eksportuję nowy komponent AppContainer
, który powstaje w wyniku wywołania funkcji connect
(3).
Pozostaje tylko użyć już nowopowstałego komponentu. W index.js zamiast <App />
używam <AppContainer />
.
Nic się nie zmieniło
Zaiste. Nic się nie zmieniło, bo jeszcze nie użyłem nidzie action creatora (nadążasz?). Usuwam state
z komponentu App
i zamiast tego polegam tylko na action creatorach i storze:
class App extends React.Component {
componentDidMount() {
fetch("https://randomuser.me/api/?format=json&results=10")
.then(res => res.json())
.then(json => this.props.contactsFetched(json.results)); // (1)
}
render() {
return (
<div>
<AppHeader />
<main className="ui main text container">
<ContactsList contacts={this.props.contacts} /> {/* (2) */}
</main>
</div>
);
}
}
Jak widzisz, zamiast setState
używam this.props.contactsFetched
(1). A do komponentu ContactsList
przekazuję this.props.contacts
(2).
Cały kod źródłowy tutaj: github.com/mmiszy/typeofweb-kurs-react/tree/contacts-list-2-redux
Przedstawiony tutaj sposób na pracę z asynchronicznymi funkcjami i Reduksem jest najprotszym z możliwych. W kolejnych wpisach przedstawię inne metody obsługi *asynchroniczności w Redux*.
Podsumowanie
Czujesz się podtopiona/y? Taki był zamiar. Napisaliśmy mnóstwo kodu, a efekt jest taki sam, jak wcześniej (a nawet gorszy!). Po co to wszystko?
Szczęśliwie ten boilerplate pisze się tylko raz. A rozbudowa tej aplikacji będzie teraz znacznie prostsza! Zobaczysz sam(a) w kolejnym wpisie 🙂 Sprawdź szkolenia z React i Redux!
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: Dodaj do aplikacji napis „Ładowanie” w trakcie pobierania danych z API. Nie używaj .length
😉
Hint: Dodaj nową własność w storze, która będzie przechowywała informację o tym czy trwa pobieranie.
- 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 + Redux — kurs: wprowadzenie i podstawy […]
czy zapis funkcji: const mapDispatchToProps = { contactsFetched };
jest tożsamy z:
const mapDispatchToProps = (dispatch) => {
return {
setData: () => dispatch( contactsFetched )
};
??? w paru miejscach spotkałem się z takim zapisem…
Jest to funkcjonalnie to samo co:
Fajnie opisujesz React i elementy towarzyszące jak np. redux. Moim zdaniem masz dobry język pisania dla początkujących i wielu na pewno skorzysta. Może warto więc jakiś jeden wpis poświęcić np. na Redux Dev Tools co jest bardzo fajną rzeczą zamiast pisania czasami miliona console.log 🙂 i może coś o webpack 🙂 Ale to takie bardziej propozycje przyszłych tematów 🙂
Wytrwałości życzę w dalszym tworzeniu fajnego kursu!
także podpisuje się pod Webpack potem
taki od podstaw do wersji prod 🙂
niby wszystko zrobilem jak jest w tutorialu i github a dostaje errorem po twarzy na koniec. TypeError: __WEBPACK_IMPORTED_MODULE_0_react___default.a.createContext is not a function
Tak jak tutaj? https://github.com/mmiszy/typeofweb-kurs-react/tree/contacts-list-2-redux
nic tu nie widze. zalinkowałeś do swojego githuba
Zalinkowałem do kodu z tego odcinka kursu. Możesz porównać albo nawet pobrać i uruchomić u siebie.
@kacperjabco:disqus sprawdź czy na pewno masz importy te same wszędzie
[…] React + Redux — kurs: wprowadzenie i podstawy […]
Nie udalo mi sie ogarnac cwiczenia co jest pewnie efektem tego ze jeszcze nie do konca to rozumiem.
kod wyglada tak :
https://github.com/grabowskiArtur/contact_list/pull/new/AddRedux
Przede wszystkim nie bardzo wiem co mam dac jako return w index.js w actions. Celem oczywiscie jest wyswietlelnie na stronce stringa ze to sie laduje. Reszta „na czuja” jest chyba ok ale mam watpliwosci co do przekazywania tego wszytkiego w pliku App
Nie potrzebujesz nowe akcji. W mapStateToProps sprawdź, czy istnieje state.contacts i jeśli nie to ustaw isLoading true, a jeśli tak to isLoading false.
Dziala, dzieki 🙂 Kurde to bylo takie proste…. 🙁