Ten artykuł krótko opisze jak zbudować silnik maszyny stanów podobny do Redux.
Więc krótko, czego potrzebujemy do zdefiniowania maszyny stanów? - Sam stan (metoda dostarczająca stan) - Metoda subscribe (która subskrybuje zmiany stanu) - Metoda dispatch (która wykonuje zmiany w naszym silniku stanu)
Silnik podobny do Redux - Krok 1 - Szkic funkcji zarządzania stanem
Zdefiniujmy szkic funkcji podobnej do Redux z wcześniej zdefiniowanymi metodami getState, dispatch i subscribe. Parametrem naszej funkcji zarządzającej stanem będzie reducer - funkcja opisująca jak stan powinien się zachowywać przy danej akcji.
const createStore = (reducer) => {
let state;
const getState = () => state;
const dispatch = () => console.log('ta metoda wykona zmianę stanu');
const subscribe = () => console.log('ta metoda zasubskrybuje zmiany stanu');
return {
getState,
dispatch,
subscribe
}
};
Zdefiniowana funkcja strzałkowa zwróci trzy metody: 1. getState - która zwróci nasz aktualny stan 2. dispatch - dispatcher akcji 3. subscribe - metoda subskrybująca zmiany stanu
Wewnątrz funkcji createStore zdefiniowaliśmy state. To jest zmienna przechowująca nasz aktualny stan. Jeśli chcemy mieć bezpośredni dostęp do state, musimy wywołać metodę getState.
Silnik podobny do Redux - Krok 2 - Stan początkowy
Nasz silnik podobny do Redux nie będzie działał bez reducer i stanu początkowego. W tej sekcji zdefiniujmy stan początkowy. Wyobraźmy sobie, że rozwijamy grę i jedna akcja gry będzie związana ze strzałami wykonywanymi przez użytkownika. Więc gdy użytkownik będzie klikał na ekran lub dotykał ekranu na urządzeniach mobilnych, ta akcja zostanie wywołana. Po wywołaniu tej akcji nasz stan zostanie zmieniony. W tym przypadku liczba strzałów wykonanych przez użytkownika zostanie zwiększona. Początkowo będzie równa 0.
initialState = {
shots: 0
};
Silnik podobny do Redux - Krok 3 - Reducer
W tej sekcji zdefiniujemy nasz reducer, który opisze jak nasz stan powinien być zmieniony po dispatchu / wykonaniu danej akcji. Reducer otrzyma dwa parametry: 1. Stan początkowy (zdefiniowany wcześniej) 2. Akcja
Akcja to obiekt z dwoma parametrami: 1. type 2. payload
Na początek opiszemy jak nasz stan powinien się zachowywać gdy użytkownik wywoła akcję SHOT.
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'SHOT':
state = {
...state,
shots: state.shots +1
}
return state;
default:
return state;
}
}
Aby zinterpretować naszą funkcję reducer, wyobraźmy sobie, że chcemy wywołać akcję SHOT. Aby to zrobić, musimy przekazać do reducer obiekt akcji taki jak
{
type: 'SHOT'
}
Silnik podobny do Redux - Krok 4 - Ciało funkcji zarządzania stanem
Sprawmy, aby nasz stan był użyteczny. Aby to zrobić, musimy zdefiniować dwie metody wewnątrz niego 1. subscribe 2. dispatch
Zanim funkcja subscribe zostanie utworzona, musimy zdefiniować listeners wewnątrz createStore. Listeners to tablica elementów, które będą subskrybować nasz stan i reagować na każdą zmianę stanu.
const createStore = (rootReducer) => {
let state;
let listeners = [];
const getState = () => state;
const dispatch = action => {
state = rootReducer(state, action);
listeners.forEach(listener => listener(state));
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener)
}
};
return {
getState,
dispatch,
subscribe
}
};
Silnik podobny do Redux - Krok 5 - Połącz i uruchom
Zróbmy krótki test. W tym podrozdziale połączymy reducer i nasz silnik zarządzania stanem. Na końcu kodu stworzymy instancję store, zasubskrybujemy ją i pobierzemy jej wartość.
const initialState = {
shots: 0
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'SHOT':
console.log('shot')
state = {
...state,
shots: state.shots +1
}
return state;
default:
return state;
}
};
const createStore = (rootReducer) => {
let state;
let listeners = [];
const getState = () => state;
const dispatch = action => {
state = rootReducer(state, action);
listeners.forEach(listener => listener(state));
};
const subscribe = (listener) => {
listeners.push(listener);
return () => {
listeners = listeners.filter(l => l !== listener)
}
};
return {
getState,
dispatch,
subscribe
}
};
const storeInstance = createStore(reducer);
console.log(storeInstance.getState());
storeInstance.subscribe(() => console.log('subscriber shots:', + storeInstance.getState().shots))
storeInstance.dispatch({type: 'SHOT'});
storeInstance.dispatch({type: 'SHOT'});
storeInstance.dispatch({type: 'SHOT'});
console.log(storeInstance.getState());
Po uruchomieniu tego kodu zobaczymy log konsoli podobny do tego:
"shot"
"subscriber shots:" 1
"shot"
"subscriber shots:" 2
"shot"
"subscriber shots:" 3
[object Object] {
shots: 3
}
Za każdym razem gdy funkcja "dispatch" jest uruchamiana, daje nam informację o sumie strzałów. Dodatkowo możemy zobaczyć, że funkcja reducer jest uruchamiana (log "shot").
Zarządzanie stanem - Czy to nie jest łatwe? :D




