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 => { if (typeof action === 'function'){ //если action - не объект, а функция return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы } 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 promiseReducer(state = {}, {promiseName, type, status, payload, error}) { if (type === 'PROMISE') { return {...state, [promiseName]: {status, payload, error}} } return state } const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms)); const actionPending = promiseName => ({promiseName, type: 'PROMISE', status: 'PENDING'}) const actionFulfilled = (promiseName, payload) => ({promiseName, type: 'PROMISE', status: 'FULFILLED', payload}) const actionRejected = (promiseName, error) => ({promiseName, type: 'PROMISE', status: 'REJECTED', error}) const actionPromise = (promiseName, promise) => async dispatch => { dispatch(actionPending(promiseName)) //сигнализируем redux, что промис начался try { const payload = await promise //ожидаем промиса dispatch(actionFulfilled(promiseName, payload)) //сигнализируем redux, что промис успешно выполнен return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса } catch (error) { dispatch(actionRejected(promiseName, error)) //в случае ошибки - сигнализируем redux, что промис несложился } } const store = createStore(promiseReducer) store.subscribe(() => console.log(store.getState())) //должен запускаться 6 раз store.dispatch(actionPromise('delay', delay(1000))) store.dispatch(actionPromise('luke', fetch("https://swapi.dev/api/people/1").then(res => res.json()))) store.dispatch(actionPromise('tatooine', fetch("https://swapi.dev/api/planets/1").then(res => res.json()))) ///////////////////////////////////////////////////////////////////// function authReducer (state={}, {token, type}){ if (type === 'AUTH_LOGIN') { try { let str = token.split('.')[1]; let result = JSON.parse(atob(str)) return {...state, 'token': token, 'playload':result} } catch (e) { return {} } } else if (type === 'AUTH_LOGOUT') { return {} } return state } const actionAuthLogin = token => ({type: 'AUTH_LOGIN', token}) const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'}) const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2Mzc3ZTEzM2I3NGUxZjVmMmVjMWMxMjUiLCJsb2dpbiI6InRlc3Q1IiwiYWNsIjpbIjYzNzdlMTMzYjc0ZTFmNWYyZWMxYzEyNSIsInVzZXIiXX0sImlhdCI6MTY2ODgxMjQ1OH0.t1eQlRwkcP7v9JxUPMo3dcGKprH-uy8ujukNI7xE3A0" const storeAuth = createStore(authReducer) storeAuth.subscribe(() => console.log(storeAuth.getState())) storeAuth.dispatch(actionAuthLogin(token)) // /////////////////////////////////////////////////////////////////////// function cartReducer (state = {}, {type, good, count}) { let goodKey, oldCount, goodValue; if (good) { goodKey = good['_id']; oldCount = state[goodKey]?.count || 0; goodValue = {good, count: oldCount} } if (type === 'CARD_ADD') { goodValue.count += count; return {...state, [goodKey]: goodValue} } else if (type === 'CART_DEL') { delete state[goodKey]; } } const actionCartAdd = (good, count=1) => ({type: 'CART_ADD', count, good}) const actionCartSub = (good, count=1) => ({type: 'CART_SUB', count, good}) const actionCartDel = (good) => ({type: 'CART_DEL', good}) const actionCartSet = (good, count=1) => ({type: 'CART_SET', count, good}) const actionCartClear = () => ({type: 'CART_CLEAR'}) /////////////////////////////////////////////////////////////////