Як впровадити Redux у 24 рядки JavaScript

90% конвенції, 10% бібліотеки.

Redux є однією з найважливіших бібліотек JavaScript, коли-небудь створених. Натхненний такими рівнями мистецтва, як Flux та Elm, Redux розмістив на карті функціональне програмування JavaScript, представивши масштабовану архітектуру з трьох простих точок.

Якщо ви новачок у Redux, спершу прочитайте офіційні документи.

Redux - це переважно конвенція

Розглянемо цю просту програму-лічильник, яка використовує архітектуру Redux. Якщо ви хочете перейти вперед, перевірте репозитарій Github для нього.

redux-counter-app-demo

Держава живе в одному дереві

Стан програми виглядає так.

const initialState = { count: 0 }; 

Дії оголошують зміни стану

Згідно з конвенцією Redux, я безпосередньо не змінюю (мутую) стан.

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

Натомість я створюю всі дії, які користувач може використовувати у програмі.

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

Редуктор інтерпретує дії та оновлює стан

Останній архітектурний фрагмент вимагає редуктора, чистої функції, яка повертає нову копію вашого стану на основі попереднього стану та дії.

  • Якщо incrementспрацьовує, збільшуйте state.count.
  • Якщо decrementзвільнено, зменшити 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; } }; 

Поки що немає Redux

Ви помітили, що ми ще не торкалися бібліотеки Redux? Ми щойно створили деякі об’єкти та функції. Це те, що я маю на увазі під "переважно конвенцією", 90% Redux не потребує Redux!

Давайте реалізуємо Redux

Щоб застосувати цю архітектуру, ми повинні підключити її до магазину. Ми реалізуємо лише одну функцію– createStore.

Це використовується так.

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 } 

І ось наш початковий шаблон. Нам знадобиться список слухачів та початковий стан, який надає редуктор.

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

Щоразу, коли хтось підписується на наш магазин, він додається до listenersмасиву. Це важливо, тому що кожного разу, коли хтось надсилає дію, про це listenersпотрібно повідомити у циклі.

Виклик yourReducerіз undefinedпорожнім об'єктом повертає initialStateвстановлене вище. Це дає нам належне значення для повернення, коли ми дзвонимо store.getState(). Говорячи про це, давайте створимо цей метод.

store.getState ()

Це функція, яка повертає останній стан із магазину. Це нам знадобиться для оновлення нашого інтерфейсу кожного разу, коли користувач натискає кнопку.

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

store.dispatch (дія)

Це функція, яка приймає actionяк параметр. Харчується , що actionі currentStateдля , yourReducerщоб отримати нове стан. Потім dispatchповідомляє всіх, хто підписався на 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 (слухач)

Це функція, яка дозволяє отримувати сповіщення, коли магазин отримує дію. Тут добре використовувати store.getState()тут, щоб отримати останній стан та оновити інтерфейс.

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

subscribeповертає функцію з назвою, unsubscribeяку ви можете викликати, коли вам більше не цікаво слухати оновлення магазину.

Зараз усі разом

Давайте підключимо це до наших кнопок і переглянемо остаточний вихідний код.

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

І ще раз ось наш остаточний інтерфейс.

redux-counter-app-demo

Якщо вас цікавить HTML / CSS, яким я користувався, ось знову репо GitHub!

Хочете безкоштовно коучинг?

Якщо ви хочете призначити безкоштовний дзвінок для обговорення питань розвитку Front-End щодо коду, інтерв’ю, кар’єри чи чогось іншого, слідкуйте за мною у Twitter та DM.

Після цього, якщо вам сподобається наша перша зустріч, ми можемо обговорити постійний тренінг, який допоможе вам досягти ваших цілей розвитку Front-End!

Носіть свій внесок

Якщо ви кодуєте кожен день, особливо якщо ви зобов'язуєтесь GitHub, чи не було б здорово носити цю карту внесків, щоб усі бачили?

Gitmerch.com дозволяє надрукувати футболку вашої карти внесків GitHub! Використовуйте код, Yazeed , при оформленні замовлення, щоб отримати знижку.

git-merch-screenshot-1-1

git-merch-screenshot-2-1

Дякуємо за читання

Щоб отримати більше подібного вмісту, перегляньте //yazeedb.com!

До наступного разу!