Comment implémenter Redux en 24 lignes de JavaScript

90% convention, 10% bibliothèque.

Redux fait partie des bibliothèques JavaScript les plus importantes jamais créées. Inspiré par l'art antérieur comme Flux et Elm, Redux a mis la programmation fonctionnelle JavaScript sur la carte en introduisant une architecture évolutive de trois points simples.

Si vous êtes nouveau sur Redux, pensez à lire d'abord la documentation officielle.

Redux est principalement une convention

Considérez cette application de compteur simple qui utilise l'architecture Redux. Si vous souhaitez aller de l'avant, consultez le dépôt Github pour cela.

redux-counter-app-demo

L'État vit dans un seul arbre

L'état de l'application ressemble à ceci.

const initialState = { count: 0 }; 

Les actions déclarent des changements d'état

Par convention Redux, je ne modifie pas (mute) directement l'état.

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

Au lieu de cela, je crée toutes les actions que l'utilisateur peut exploiter dans l'application.

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

Le réducteur interprète l'action et met à jour l'état

Le dernier élément architectural appelle un réducteur, une fonction pure qui renvoie une nouvelle copie de votre état en fonction de l'état et de l'action précédents.

  • Si incrementest déclenché, incrémenter state.count.
  • Si decrementest déclenché, décrémentez 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; } }; 

Pas de Redux pour l'instant

Avez-vous remarqué que nous n'avons pas encore touché à la bibliothèque Redux? Nous venons de créer des objets et une fonction. C'est ce que j'entends par «principalement convention», 90% de Redux ne nécessite pas Redux!

Implémentons Redux

Pour mettre cette architecture à profit, il faut la brancher dans un magasin. Nous n'implémenterons qu'une seule fonction - createStore.

C'est utilisé comme ça.

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 } 

Et voici notre passe-partout initial. Nous aurons besoin d'une liste d'écouteurs et de l'état initial fourni par le réducteur.

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

Chaque fois que quelqu'un s'abonne à notre boutique, il est ajouté au listenerstableau. Le est important car chaque fois que quelqu'un distribue une action, tous listenersdoivent être notifiés en boucle.

L'appel yourReduceravec undefinedet un objet vide renvoie le que initialStatenous avons installé ci-dessus. Cela nous donne une valeur appropriée à renvoyer lorsque nous appelons store.getState(). En parlant de cela, créons cette méthode.

store.getState ()

Il s'agit d'une fonction qui renvoie le dernier état du magasin. Nous en aurons besoin pour mettre à jour notre interface utilisateur chaque fois que l'utilisateur clique sur un bouton.

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

store.dispatch (action)

Il s'agit d'une fonction qui prend un actioncomme paramètre. Il se nourrit que actionet currentStatepour yourReducerobtenir un nouvel état. Puis dispatchinforme tous les abonnés au 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 (auditeur)

C'est une fonction qui vous permet d'être averti lorsque le magasin reçoit une action. Il est bon de l'utiliser store.getState()ici pour obtenir votre dernier état et mettre à jour votre interface utilisateur.

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; } }; }; 

subscriberenvoie une fonction appelée unsubscribeque vous pouvez appeler lorsque vous n'êtes plus intéressé à écouter les mises à jour du magasin.

Tous ensemble maintenant

Accrochons ceci à nos boutons et visualisons le code source final.

// 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); }); 

Et encore une fois, voici notre interface utilisateur finale.

redux-counter-app-demo

Si vous êtes intéressé par le HTML / CSS que j'ai utilisé, voici à nouveau le dépôt GitHub!

Vous voulez un coaching gratuit?

Si vous souhaitez planifier un appel gratuit pour discuter des questions de développement Front-End concernant le code, les interviews, la carrière ou toute autre chose, suivez-moi sur Twitter et envoyez-moi un SMS.

Après cela, si vous appréciez notre première réunion, nous pouvons discuter d'un coaching continu pour vous aider à atteindre vos objectifs de développement Front-End!

Portez vos contributions

Si vous codez tous les jours, surtout si vous vous engagez sur GitHub, ne serait-il pas cool de porter cette carte de contribution à la vue de tous?

Gitmerch.com vous permet d'imprimer un t-shirt de votre carte de contribution GitHub! Utilisez le code, Yazeed , à la caisse pour une réduction.

git-merch-screenshot-1-1

git-merch-screenshot-2-1

Merci d'avoir lu

Pour plus de contenu comme celui-ci, consultez //yazeedb.com!

Jusqu'à la prochaine fois!