Sådan implementeres Redux i 24 linjer med JavaScript

90% konvention, 10% bibliotek.

Redux er blandt de vigtigste JavaScript-biblioteker, der nogensinde er oprettet. Inspireret af kendt teknik som Flux og Elm satte Redux JavaScript-funktionel programmering på kortet ved at introducere en skalerbar arkitektur med tre enkle punkter.

Hvis du er ny i Redux, skal du overveje at læse de officielle dokumenter først.

Redux er hovedsagelig konvention

Overvej denne enkle modapplikation, der bruger Redux-arkitekturen. Hvis du gerne vil springe frem, skal du tjekke Github-repoen for det.

redux-counter-app-demo

Staten bor i et enkelt træ

Applikationens tilstand ser sådan ud.

const initialState = { count: 0 }; 

Handlinger erklærer tilstandsændringer

Ved Redux-konvention ændrer (muterer) jeg ikke staten direkte.

// DON'T do this in a Redux app state.count = 1; 

I stedet opretter jeg alle de handlinger, som brugeren kan udnytte i applikationen.

const actions = { increment: { type: 'INCREMENT' }, decrement: { type: 'DECREMENT' } }; 

Reducer fortolker handling og opdateringstilstand

Det sidste arkitektoniske stykke kræver en reducering, en ren funktion, der returnerer en ny kopi af din tilstand baseret på den tidligere tilstand og handling.

  • Hvis incrementfyres, øges state.count.
  • Hvis decrementfyres, mindskes state.count.
const countReducer = (state = initialState, action) => { switch (action.type) { case actions.increment.type: return { count: state.count + 1 }; case actions.decrement.type: return { count: state.count - 1 }; default: return state; } }; 

Ingen Redux hidtil

Har du bemærket, at vi endnu ikke har rørt ved Redux-biblioteket? Vi har lige oprettet nogle objekter og en funktion. Dette er hvad jeg mener med "for det meste konvention", 90% af Redux kræver ikke Redux!

Lad os implementere Redux

For at bruge denne arkitektur skal vi tilslutte den til en butik. Vi implementerer kun en funktion– createStore.

Det bruges sådan her.

import { createStore } from 'redux' const store = createStore(countReducer); store.subscribe(() => { console.log(store.getState()); }); store.dispatch(actions.increment); // logs { count: 1 } store.dispatch(actions.increment); // logs { count: 2 } store.dispatch(actions.decrement); // logs { count: 1 } 

Og her er vores første kogeplade. Vi har brug for en liste over lyttere og den oprindelige tilstand leveret af reduceringsenheden.

const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); } 

Når nogen abonnerer på vores butik, føjes de til listenersarrayet. Det er vigtigt, fordi hver gang nogen sender en handling, skal alt listenersmeddeles i en løkke.

Når du ringer yourReducertil undefinedog et tomt objekt, returneres det, initialStatevi installerede ovenfor. Dette giver os en passende værdi at vende tilbage, når vi ringer store.getState(). Når vi taler om, lad os oprette denne metode.

store.getState ()

Dette er en funktion, der returnerer den seneste tilstand fra butikken. Vi har brug for dette for at opdatere vores brugergrænseflade, hver gang brugeren klikker på en knap.

const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); return { getState: () => currentState }; } 

store.dispatch (handling)

Dette er en funktion, der tager en actionsom parameter. Det føder det actionog det currentStatefor yourReducerat få en ny stat. Derefter dispatchunderretter alle, der abonnerer på store.

const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); return { getState: () => currentState, dispatch: (action) => { currentState = yourReducer(currentState, action); listeners.forEach((listener) => { listener(); }); } }; }; 

store.subscribe (lytter)

Dette er en funktion, der lader dig blive underrettet, når butikken modtager en handling. Det er godt at bruge store.getState()herinde for at få din seneste tilstand og opdatere din brugergrænseflade.

const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); return { getState: () => currentState, dispatch: (action) => { currentState = yourReducer(currentState, action); listeners.forEach((listener) => { listener(); }); }, subscribe: (newListener) => { listeners.push(newListener); const unsubscribe = () => { listeners = listeners.filter((l) => l !== newListener); }; return unsubscribe; } }; }; 

subscribereturnerer en funktion kaldet, unsubscribesom du kan ringe til, når du ikke længere er interesseret i at lytte til butikens opdateringer.

Alt sammen nu

Lad os tilslutte dette til vores knapper og se den endelige kildekode.

// simplified createStore function const createStore = (yourReducer) => { let listeners = []; let currentState = yourReducer(undefined, {}); return { getState: () => currentState, dispatch: (action) => { currentState = yourReducer(currentState, action); listeners.forEach((listener) => { listener(); }); }, subscribe: (newListener) => { listeners.push(newListener); const unsubscribe = () => { listeners = listeners.filter((l) => l !== newListener); }; return unsubscribe; } }; }; // Redux architecture pieces const initialState = { count: 0 }; const actions = { increment: { type: 'INCREMENT' }, decrement: { type: 'DECREMENT' } }; const countReducer = (state = initialState, action) => { switch (action.type) { case actions.increment.type: return { count: state.count + 1 }; case actions.decrement.type: return { count: state.count - 1 }; default: return state; } }; const store = createStore(countReducer); // DOM elements const incrementButton = document.querySelector('.increment'); const decrementButton = document.querySelector('.decrement'); // Wire click events to actions incrementButton.addEventListener('click', () => { store.dispatch(actions.increment); }); decrementButton.addEventListener('click', () => { store.dispatch(actions.decrement); }); // Initialize UI display const counterDisplay = document.querySelector('h1'); counterDisplay.innerHTML = parseInt(initialState.count); // Update UI when an action fires store.subscribe(() => { const state = store.getState(); counterDisplay.innerHTML = parseInt(state.count); }); 

Og her er endnu en gang vores sidste brugergrænseflade.

redux-counter-app-demo

Hvis du er interesseret i HTML / CSS, jeg brugte, her er GitHub-repoen igen!

Vil du have gratis coaching?

Hvis du vil planlægge et gratis opkald for at diskutere Front-End-udviklingsspørgsmål vedrørende kode, interviews, karriere eller andet, følg mig på Twitter og DM mig.

Derefter, hvis du nyder vores første møde, kan vi diskutere en løbende coaching for at hjælpe dig med at nå dine Front-End-udviklingsmål!

Bær dine bidrag

Hvis du koder hver dag, især hvis du forpligter dig til GitHub, ville det ikke være sejt at bære dette bidragskort for alle at se?

Gitmerch.com giver dig mulighed for at udskrive en t-shirt af dit GitHub-bidragskort! Brug koden Yazeed ved kassen for at få rabat.

git-merch-screenshot-1-1

git-merch-screenshot-2-1

Tak for læsningen

For mere indhold som dette, se //yazeedb.com!

Indtil næste gang!