function reducer(state, { type, name, amount, money }) { //объект action деструктуризируется на три переменных if (!state) { //начальная уборка в ларьке: return { products: { пиво: { amount: 100, price: 30, }, чипсы: { amount: 100, price: 25, }, сиги: { amount: 100, price: 35, } }, balance: { amount: 0 } } } if (type === 'buy') { //если тип action - КУПИТЬ, то: if (amount > state.products[name].amount) { alert('You have entered more quantity than is available'); return { ...state, } } if (money < amount * state.products[name].price) { alert('You don`t have enought money'); return { ...state, } } if (money > amount * state.products[name].price) { alert('You gave more money'); return { ...state, } } let updatedProducts = state.products let updatedField = updatedProducts[name]; updatedField.amount -= amount; let updatedBalance = state.balance; updatedBalance.amount += +money; return { ...state, //берем все что было из ассортимента products: updatedProducts, //и уменьшаем то, что покупается на количество balance: updatedBalance, } } return state; //если мы не поняли, что от нас просят в `action` - оставляем все как есть } function createStore(reducer) { let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined let cbs = []; //массив подписчиков const getState = () => state; //функция, возвращающая переменную из замыкания const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве () => cbs = cbs.filter(c => c !== cb)); //возвращаем функцию unsubscribe, которая удаляет подписчика из списка const dispatch = action => { const newState = reducer(state, action); //пробуем запустить редьюсер if (newState !== state) { //проверяем, смог ли редьюсер обработать action state = newState; //если смог, то обновляем state for (let cb of cbs) cb(); //и запускаем подписчиков } } return { getState, //добавление функции getState в результирующий объект dispatch, subscribe //добавление subscribe в объект } } const store = createStore(reducer); //запомнит функцию во внутреннем массиве cbs. //она будет запущена при любом успешном dispatch const unsubscribe = store.subscribe(() => console.log(store.getState())); setTimeout(unsubscribe, 10000); //отпишемся через 10 секунд, например const buyAction = (name, amount, money) => ({ type: 'buy', name, amount, money }); //происходит запуск редьюсера, который создает новый state. //dispatch запускает всех подписчиков из массива cbs function createTable() { let state = store.getState(); let table = document.getElementById('table'); table.innerHTML = ''; let headerRow = document.createElement('tr'); table.append(headerRow); let headerName = document.createElement('th'); headerName.innerText = 'Name'; headerRow.append(headerName); let headerAmount = document.createElement('th'); headerAmount.innerText = 'Amount'; headerRow.append(headerAmount); let headerPrice = document.createElement('th'); headerPrice.innerText = 'Price'; headerRow.append(headerPrice); Object.keys(state.products).forEach(key => { let row = document.createElement('tr'); table.append(row); let name = document.createElement('td'); name.innerText = key; row.append(name); let amount = document.createElement('td'); amount.innerText = state.products[key].amount; row.append(amount); let price = document.createElement('td'); price.innerText = state.products[key].price; row.append(price); }); } function createBalance() { let state = store.getState(); let balance = document.getElementById('balance'); balance.innerHTML = ''; balance.innerText = `Balance: ${state.balance.amount}`; } function createForm() { let state = store.getState(); let form = document.getElementById('form'); form.innerHTML = ''; let selectLabel = document.createElement('label'); selectLabel.innerText = 'Select a product:'; form.append(selectLabel); let select = document.createElement('select'); form.append(select); Object.keys(state.products).forEach(key => { let option = document.createElement('option'); option.innerHTML = key; select.append(option); }); let inputLabel = document.createElement('label'); inputLabel.innerText = 'Enter quantity:'; form.append(inputLabel); let amountInput = document.createElement('input'); form.append(amountInput); let moneyInputLabel = document.createElement('label'); moneyInputLabel.innerText = 'Enter amount:'; form.append(moneyInputLabel); let moneyInput = document.createElement('input'); form.append(moneyInput); let submitButton = document.createElement('button'); submitButton.innerText = 'Buy'; form.append(submitButton); submitButton.addEventListener('click', () => { store.dispatch(buyAction(select.value, amountInput.value, moneyInput.value)); init(); }) } function init() { createTable(); createBalance(); createForm(); } init();