Wprowadzenie do Redux’a

Koncepcja sterowania zmianami poprzez stany w bibliotece Redux została zastosowana do całej aplikacji. Trzeba jednak odróżnić stan (state) Reduxa i stan (state) w komponentach. W pierwszym przypadku state to wszystkie informacje o stanie aplikacji: np. jakie dane zostały już pobrane z serwera, jaka jest zawartość formularza, jaki użytkownik jest zalogowany, jakie okienko jest otwarte. Stan komponentu jest bardzo „lokalny” (dotyczy tylko komponentu)1. Redux służy do jednolitego przetwarzania stanu całej aplikacji.

Obiekt zarządzający stanem aplikacji jest nazywany store. Składa się on z aktualnego stanu (state - klasyczny obiekt JavaScript) oraz funkcji reducer, która na podstawie obecnego stanu i wykonanej akcji wylicza następny.

Przykład defnicji store:

import { createStore, } from 'redux';

export type StanAplikacji = {
  licznik: number,
};

export const ACTION_ZWIEKSZ = 'ACTION_ZWIEKSZ'

const reducer = (state = { licznik: 0, }, action) => {
  switch (action.type) {
    case ACTION_ZWIEKSZ:
      return Object.assign({}, state, {
        licznik : state.licznik + 1,
      });
    default: 
      return state;
  }
}

export const store = createStore( reducer, );

export const Akcje = {
  zwieksz(): void {
    store.dispatch({type: ACTION_ZWIEKSZ});
  }
}

Rozłóżmy na czynniki pierwsze reducer’a:

  • const reducer = <definicja funkcji> - zdefiniowanie stałej zawierającej funkcję reducera.

  • <definicja funkcji> ::= (<parametry>) => { <akcje> }

  • <parametry> ::= state, action – przy czyn state otrzymuje domyślnie wartość { licznik: 0, }

  • <akcje> - kod w JavaScript – wykorzystuje Object.assign (zob. https://googlechrome.github.io/samples/object-assign-es6/); użycie {} jako pierwszego parametru zapewnia, że dostaniemy klon obiektu (state)

Użycie jest banalnie proste (porównując z poprzednimi przykładami widać, że Redux choć wydaje się koncepcyjnie trudny - znacznie ułatwia życie):

import React, { Component } from 'react';
import { connect } from 'react-redux';
import type { Akcje, StanAplikacji } from './store.js';

class App extends Component {

  render() {return ( 

  <button onClick = { () => Akcje.zwieksz() } > 
     Kliknij  {this.props.dane.licznik.toString()}  
  </button>

    ); }
}

export default connect(
  (state: StanAplikacji) => ({ dane: state }),
)(App);

Objaśnienie:

  1. Zastosowano odrębny obiekt na definicje akcji (Akcje) zdefiniowany wcześniej .2

  2. funkcja connect wiąże pamięć (storage) z komponem App - poprzez "wstrzyknięcie" własności "dane" (dlatego możemy użyć this.props.dane…..),

  3. Nieco zmienia się wywołanie ReactDOM:

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

Dla wnikliwego czytelnika: to, co tu się odbywa to wykorzystanie mniej znanego brata props i state: a mianowicie contextu. Context działa tak, że dany komponent może powiedzieć: „jeśli ktoś z moich pod-komponentów chciałby te dane, to one będą dostępne w this.context.cośtam”. Czyli tak jak props, ale dostępne nie tylko w komponencie do którego została przekazana własność=”wartość”, ale we wszystkich „potomkach”.

Provider robi dokładnie to: jeśli którykolwiek z komponentów chce mieć dostęp do store, to jest on dostępny w contexcie.

Odbiorcą są komponenty tworzone przez funkcję connect: one wyciągają store z contextu, wyciągają obecny stan ze store, i przekazujągo do „właściwego komponentu” w postaci propsów.

Więcej prakycznych przykładów: https://github.com/galicea/eduprog/tree/master/web/react/redux

Przypis do rozdziału:

1Może to np. być aktualne powiększenie na wyświetlanej mapie itd

2 Zobacz: http://redux.js.org/docs/basics/Actions.html#action-creators

Wbrew powszechnej praktyce następuje tu bezpośrednie wywołanie store.dispatch **z wewnątrz** tej funkcji (action creatora). Zaleca się aby zamiast tego używać funkcji dispatch, która jest wstrzykiwana do komponentów za pomocą czegoś w stylu dependency injection (w funkcji connect).

results matching ""

    No results matching ""