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 в объект } } function reducer(state, {type, ШО, СКОКА, БАБОС}){ //объект action деструктуризируется на три переменных if (!state){ //начальная уборка в ларьке: return { // пиво: 100, //{count: 100, price: 30} // чипсы: 100,//{count: 100, price: 25} пиво: {count: 100, price: 30}, чипсы: {count: 100, price: 25}, сиги: {count: 100, price: 50}, касса: 0, //при покупках увеличивается } } if (type === 'КУПИТЬ' && state[ШО] && СКОКА > 0 && СКОКА <= state[ШО].count && БАБОС >= state[ШО].price * СКОКА){ //если тип action - КУПИТЬ, то: //проверить на: //наличие товара как такового (есть ли ключ в объекте) //количество денег в action //наличие нужного количества товара. //и только при соблюдении этих условий обновлять state. console.log('bang') console.log(store.getState()) state.касса += truncateFraction(+БАБОС) display_hide_error(arguments[1]) return { ...state, //берем все что было из ассортимента [ШО]: {count: state[ШО].count - Math.floor(СКОКА), price: state[ШО].price },//и уменьшаем то, что покупается на количество } } display_hide_error(arguments[1]) return state //если мы не поняли, что от нас просят в `action` - оставляем все как есть } const store = createStore(reducer) //надо бы напилить цикл, который в select напихивает ассортимент. //возможно, если вы собираетесь выводить (и обновлять) количество, //это надо делать где в subscribe, иначе оно не будет обновлять количество let select = document.getElementById('goods') for(let key in store.getState()) { if (key === 'касса') continue; let option = document.createElement('option') option.innerText = key option.value = key select.append(option) } let quantity = document.getElementById('quantity') let cash = document.getElementById('cash') const купи = (ШО, СКОКА, БАБОС) => ({type: 'КУПИТЬ', ШО, СКОКА, БАБОС}) buy.onclick = () => { //достает выбранный товар и количество из DOM // store.dispatch(купи(....,....)) store.dispatch(купи(select.value, quantity.value, cash.value)) } //запомнит функцию во внутреннем массиве cbs. //она будет запущена при любом успешном dispatch const unsubscribe = store.subscribe(() => console.log(store.getState())) //setTimeout(unsubscribe, 10000) //отпишемся через 10 секунд, например //происходит запуск редьюсера, который создает новый state. //dispatch запускает всех подписчиков из массива cbs let errorP = document.createElement('p') errorP.style.color = 'red' errorP.style.textAlign = 'center' shop.append(errorP) function truncateFraction (number) { try { let strNum = String(number).split('.') if(strNum[1].length > 2) { strNum[1] = strNum[1].slice(0, 2) } return Number(strNum.join('.')) } catch (e) { return number } } function display_hide_error(obj) { errorP.innerText = '' if (obj.СКОКА === '' && obj.БАБОС === '') { errorP.innerText = 'Мысли я читать не умeю >:^[' return; } else if(obj.СКОКА === '' || obj.СКОКА < 0) { errorP.innerText = 'А единиц товара сколько брать будете?' return; } if (obj.СКОКА > store.getState()[obj.ШО].count) { errorP.innerText = `У нас нет столько ${obj.ШО}. ${obj.ШО} осталось ${store.getState()[obj.ШО].count}` return; } else if (obj.БАБОС < store.getState()[obj.ШО].price * obj.СКОКА) { errorP.innerText = `Недостаточно денег для покупки ${obj.СКОКА} ед. ${obj.ШО}. ${obj.ШО} стоит ${store.getState()[obj.ШО].price} денег за 1 ед. товара Стоимость вашего заказа ${store.getState()[obj.ШО].price * obj.СКОКА}` return; } } let table = document.createElement('table') function drawTable(){ table.innerHTML = '' table.insertAdjacentHTML('afterbegin', ` Товар Кол-во Стоимость `) for(let key in store.getState()) { if(key === 'касса') continue; let tr = document.createElement('tr') let tdKey = document.createElement('td') let tdCount = document.createElement('td') let tdCost = document.createElement('td') tdKey.innerText = key tdCount.innerText = store.getState()[key].count tdCost.innerText = `${store.getState()[key].price} денег` tr.append(tdKey, tdCount, tdCost) table.append(tr) } table.insertAdjacentHTML('beforeend', ` Касса ${store.getState().касса} денег `) shop.append(table) } drawTable() store.subscribe(drawTable) console.log(store.getState())