<header>reducers</header> <body> <div id="testDiv"> Test </div> <script> function promiseReducer(state = {}, action) { // диспетчер обработки if (action) { if (action.type === 'PROMISE') { let newState = { ...state }; newState[action.name] = { status: action.status, payload: action.payload, error: action.error }; return newState; } } return state; } 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, которая удаляет подписчика из списка function 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 actionPromise(name, promise) { return async function Exec(dispatch) { dispatch(actionPending(name)) //сигнализируем redux, что промис начался try { const payload = await promise //ожидаем промиса; dispatch(actionFulfilled(name, payload)); //сигнализируем redux, что промис успешно выполнен return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса } catch (error) { dispatch(actionRejected(name, error)) //в случае ошибки - сигнализируем redux, что промис несложился } }; } const actionPending = (name) => ({ type: 'PROMISE', name: name, status: 'PENDING' }); const actionFulfilled = (name, payload) => ({ type: 'PROMISE', name: name, payload: payload, status: 'FULFILLED' }); const actionRejected = (name, error) => ({ type: 'PROMISE', name: name, error: error, status: 'REJECTED' }); const store = createStore(promiseReducer); store.subscribe(() => { console.log(store.getState()) }); store.dispatch(actionPromise('luke', fetch("https://swapi.dev/api/people/1").then(res => res.json()))) //let execFunc = actionPromise({ name: "auth", promise: signIn("test457", "123123", "http://shop-roles.node.ed.asmer.org.ua/graphql") }); // store.dispatch(execFunc); /* const actionPending = () => ({ type: 'PROMISE', status: 'PENDING' }) const actionFulfilled = payload => ({ type: 'PROMISE', status: 'FULFILLED', payload }) const actionRejected = error => ({ type: 'PROMISE', status: 'REJECTED', error }) store.subscribe(() => console.log(store.getState())) store.dispatch({ type: 'COUNTER_INC' }) store.dispatch({ type: 'BOOLEAN_SET' }) store.dispatch({ type: 'COUNTER_INC' }) store.dispatch({ type: 'BOOLEAN_TOGGLE' }) store.dispatch({ type: 'COUNTER_DEC' }) store.dispatch({ type: 'ДИЧЬ' }) //не вызывает подписчика */ </script> </body>