- 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!
Higher Order Reducers — co to takiego? Gdy popracujesz dłużej z Reduksem to na pewno zauważysz pewne powtarzalne wzorce. Napisanie akcji i reducerów do obsługi API to konieczność powtórzenia bardzo podobnego kodu kilka, kilkanaście razy! Czy na pewno jest to konieczne? Z pomocą przychodzą właśnie Higher Order Reducers i kompozycja.
Definicja Higher Order Reducer
Higher Order Reducer to funkcja, która zwraca reducer i (opcjonalnie) przyjmuje reducer jako argument.
Brzmi zrozumiale? Proste HOR są… proste 😉 Ale koncept jest bardziej rozbudowany niż się może początkowo wydawać, szczególnie jeśli weźmiemy pod uwagę kompozycję!
Zastosowanie
Weźmy sobie jako przykład operowanie na API. Pobieranie danych z endpointa. Musisz obsłużyć takie akcje:
- pobieranie rozpoczęte
- pobieranie zakończone sukcesem (dane)
- błąd pobierania (błąd)
W podstawowej werji wygląda to tak jak poniżej. Action Creatory:
const dataFetchStarted = () => ({
type: "FETCH_DATA_STARTED"
});
const dataFetchSucceeded = data => ({
type: "FETCH_DATA_SUCCESS",
payload: data
});
const dataFetchErrored = error => ({
type: "FETCH_DATA_ERROR',
payload: error
});
I do tego reducer:
const data = (
state = { data: null, isLoading: false, error: null },
action
) => {
switch (action) {
case "FETCH_DATA_STARTED":
return { data: null, isLoading: true, error: null };
case "FETCH_DATA_SUCCESS":
return { data: action.payload, isLoading: false, error: null };
case "FETCH_DATA_ERROR":
return { data: null, isLoading: false, error: action.payload };
default:
return state;
}
};
Natomiast sama akcja asynchroniczna (redux-thunk), która pobierze dane wygląda tak:
export const fetchData = () => (dispatch, getState) => {
dispatch(dataFetchStarted());
fetch("endpoint")
.then(res => res.json())
.then(json => dispatch(dataFetchSucceeded(json.results)));
.catch(err => dispatch(dataFetchErrored(err)));
};
Problem
A teraz wyobraź sobie, że dane musisz pobierać, w bardzo podobny sposób, z 2, 3, 4… 10 endpointów. Czy to oznacza, że dla każdego z nich musisz zrobić kopiuj, wklej, znajdź i zamień na powyższym kodzie? Słabo…
Ale jest rozwiązanie: Higher Order Reducer
Rozwiązanie
Jak już wspomniałem, higher order reducer to taka funkcja, która zwraca reducer. W ten sposób możemy reducery tworzyć całkowicie dynamicznie. Na przykład tak:
const asyncReducerFactory = (name) => {
return (state = { data: null, isLoading: false, error: null }, action) => {
switch (action.type) {
case `FETCH_${name}_STARTED`:
return { data: null, isLoading: true, error: null };
case `FETCH_${name}_SUCCESS`:
return { data: action.payload, isLoading: false, error: null };
case `FETCH_${name}_ERROR`:
return { data: null, isLoading: false, error: action.payload };
default:
return state;
}
};
};
Jest to funkcja, która przyjmuje tylko nazwę i zwraca reducer. Nazwy akcji są tworzone dynamicznie — na podstawie podanego argumentu name
.
Jak jej użyć? Zamiast całego poprzedniego reducera napisałbym teraz tylko:
const data = asyncReducerFactory('DATA');
A jeśli chciałbym stworzyć reducer dla pobierania kontaktów? Nic prostszego:
const contacts = asyncReducerFactory('CONTACTS');
I tak dalej. Kolejne użycia nie wymagają pisania więcej boilerplate’u.
Higher Order Action Creator
No, ale nadal mam sporo boilerplate’u, prawda? Na szczęście action creator też mogę generować dynamicznie:
const asyncActionCreatorFactory = (name, thunk) => () => {
return dispatch => {
dispatch({ type: `FETCH_${name}_STARTED` });
return dispatch(thunk)
.then(data => data.json())
.then(json => dispatch({ type: `FETCH_${name}_SUCCESS`, payload: json }))
.catch(err => dispatch({ type: `FETCH_${name}_ERROR`, payload: err }));
};
};
Ponownie — na podstawie name
generuję nazwy akcji. thunk
to pewna asynchroniczna akcja, która (zakładam) wywołuje fetch
i zwraca Promise.
Jak tego używam?
const fetchContacts = asyncActionCreatorFactory(
"DATA",
(dispatch, getState) => {
return fetch("endpoint");
}
);
A dla kontaktów?
const fetchContacts = asyncActionCreatorFactory(
"CONTACTS",
(dispatch, getState) => {
return fetch(
"https://randomuser.me/api/?format=json&results=10&seed=" +
encodeURIComponent(getState().seed)
);
}
);
Tutaj dodatkowo „przemyciłem” parametr pochodzący z getState
— tak samo jak we wpisie o redux-thunk.
Podsumowanie
W taki sposób, korzystając z funkcji wyższego rzędu, można uprościć wiele rzeczy w React i Redux. Tworzenie reducerów, action creatorów, a nawet komponentów! Mam nadzieję, że będziesz już potrafiła szybko stworzyć następne akcje i reducery dla kolejnych endpointów Twojego API! Naucz się React na naszym szkoleniu!
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 kod z naszą listą kontaktów tak, aby skorzystać z napisanych w tym wpisie funkcji. Kod znajdziesz tutaj: github.com/mmiszy/typeofweb-kurs-react/tree/contacts-list-4-redux
- 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!
Będzie coś jeszcze czy na tym koniec?
Będzie po wakacjach 🙂
Cześć Michał, co w przypadku gdy w kilku reducerach powtarza się dany kod, ale tylko w części reducera. Np te akcje do fetchowania, natomiast każdy z nich ma dodatkowe, swoje własne akcje. Czy wtedy możemy jakoś stworzyć HOR dla tych 3-4 akcji i użyć go wewnątrz danego reducera?
Jeśli chodzi o reducery, to możesz mieć kilka reducerów, które obsługują tę samą akcję w różnych częściach state’u.
Możesz też do funkcji asyncReducerFactory i asyncActionCreatorFactory dodać kolejny parametr, który będzie funkcją, która zostanie wywołana w odpowiednim momencie 🙂
Super!