szczecinski.eu

szczecinski.eu

  • Kurs React
  • Zaawansowany React
  • Kurs Redux
  • Kurs ES6
  • Blog
  • Kontakt

›Intro

Intro

  • Czym jest Redux
  • Instalacja
  • Akcje
  • Reducery
  • Store
  • Przykładowa aplikacja
  • Wiele reducerów

Użycie z React

  • Redux i React
  • react-redux
  • Instalacja
  • Provider
  • connect
  • Argumenty connect

    • mapStateToProps
    • mapDispatchToProps

Dodatkowe informacje

  • Selektory
  • Middleware
  • Przykładowe middleware

    • redux-thunk
    • redux-immutable-state-invariant
  • Niemutowalne aktualizowanie stanu
  • Testowanie reduxa
  • Narzędzia

Wiele reducerów

We wszystkich przykładach użytych do tej pory wykorzystywaliśmy tylko jeden reducer. Aplikacje napisane w ten sposób szybko staną się ciężkie w utrzymaniu i rozszerzeniu a także stracą na wydajności. Redux, w odróżnieniu od flux, posiada tylko jeden globalny store (domyślnie - oczywiście nic nie stoi na przeszkodzie byśmy utworzyli więcej instancji z użyciem kolejnych createStore) na rzecz wielu reducerów.

Rozwiązaniem jest stworzenie zagłębionej struktury reducerów, z których każdy będzie odpowiedzialny za jedną - z biznesowego punktu widzenia - funkcję aplikacji. Jeżeli do naszej aplikacji, przetrzymującej do tej pory ilość wywołań akcji TICK chcemy dodać także przechowywanie akcji HELLO możemy ręcznie utworzyć "reducer nadrzędny" ("root reducer"):

const reducer = (state = 0, action) => {
  if (action.type === 'TICK') return state + 1;
  return state;
}

const helloReducer = (state = 0, action) => {
  if (action.type === 'HELLO') return state + 1;
  return state;
}

// Tworzymy obiekt opisujący strukturę naszego store oraz reducer
// obsługujący wszystkie akcje i delegujący je do każdego z pod-reducerów
const rootReducerMap = {
  counter: reducer,
  hello: helloReducer
};

const rootReducer = (state = {}, action) => {
  // Jeżeli nie jesteś obeznany z funkcją Array.prototype.reduce, wiedz tylko
  // że wywołujemy tutaj każdą z funkcji przypisanych do kluczy obiektu rootReducerMap
  // i wynik zapisujemy w obiekcie z tymi samymi kluczami

  return Object.keys(rootReducerMap).reduce((accumulator, key) => {
    accumulator[key] = rootReducerMap[key](state[key], action);
    return accumulator;
  }, {});
}

const store = createStore(rootReducer);

store.subscribe(() => {
  console.log('Dane w Reduxie: ' , store.getState())
});

store.dispatch({ type: 'TICK' }); // "{counter: 1, hello: 0}"
store.dispatch({ type: 'TOCK' }); // "{counter: 1, hello: 0}"

store.dispatch({ type: 'TICK' }); // "{counter: 2, hello: 0}"
store.dispatch({ type: 'TOCK' }); // "{counter: 2, hello: 0}"

store.dispatch({ type: 'HELLO' }); // "{counter: 2, hello: 1}"

Jak widać, dane które zwracane są przez getState odzwierciedlają strukturę, którą zdefiniowaliśmy dla naszego rootReducer. Oba reducery zostały wywołane dla każdej z akcji, ale każdy otrzymał tylko swój "kawałek" ogólnego store (jeżeli nie jesteś tego pewnym, umieść console.log(state) w każdym z reducerów).

combineReducers

Oczywiście pisanie takiej funkcjonalności za każdym razem, kiedy chcemy utworzyć strukturę zagłębionych reducerów było by mało wydajne, dlatego Redux dostarcza nam nazwany eksport - combineReducers, który robi to za nas:

import { combineReducers, createStore } from 'redux';

const reducer = (state = 0, action) => {
  if (action.type === 'TICK') return state + 1;
  return state;
}

const helloReducer = (state = 0, action) => {
  if (action.type === 'HELLO') return state + 1;
  return state;
}

const rootReducer = combineReducers({
  counter: reducer,
  hello: helloReducer  
});

const store = createStore(rootReducer);

Również w ten sposób należy pamiętać o tym, że docelowe dane reducera nie są już dostępne bezpośrednio po wywołaniu store.getState() ale są "zagnieżdżone" w kluczu odpowiadającym kluczowi z wywołania combineReducers (NIE nazwie reducera!).

console.log(store.getState()); // "{counter: 0, hello: 0}"
console.log(store.getState().counter); // "0";
← Przykładowa aplikacjaRedux i React →
  • combineReducers
Bartosz Szczeciński © 2019 Materiał dostępny na zasadach licencji MIT.