123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- // delay
- {
- const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
- }
- // createstore для тестов
- {
- 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 в объект
- }
- }
- }
- // promiseReducer
- // Улучшите promiseReducer из материала занятия, добавив возможность работать с несколькими промисами.Для этого состояние из формата { status, payload, error } должно превратиться в состояние вида:
- {
- function promiseReducer(state = {}, { type, status, payload, error, nameOfPromise }) {
- if (type === 'PROMISE') {
- return {
- ...state,
- [nameOfPromise]: { status, payload, error }
- }
- }
- return state
- }
- // Акшоны:
- // Соответственно, имя промиса будет передаваться в редьюсер как ключ в объекте action, а так же:
- const actionPending = nameOfPromise => ({ nameOfPromise, type: 'PROMISE', status: 'PENDING' }) // в actionPending - единственный параметр
- const actionFulfilled = (nameOfPromise, payload) => ({ nameOfPromise, type: 'PROMISE', status: 'FULFILLED', payload }) // как первый параметр, payload станет вторым параметром
- const actionRejected = (nameOfPromise, error) => ({ nameOfPromise, type: 'PROMISE', status: 'REJECTED', error }) // по аналогии с actionFulfilled
- const actionPromise = (nameOfPromise, promise) => // первый параметр, второй параметр - сам промис, который мы будем обрабатывать;
- async dispatch => {
- dispatch(actionPending(nameOfPromise)) //сигнализируем redux, что промис начался
- try {
- const payload = await promise //ожидаем промиса
- dispatch(actionFulfilled(nameOfPromise, payload)) //сигнализируем redux, что промис успешно выполнен
- return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
- }
- catch (error) {
- dispatch(actionRejected(nameOfPromise, error)) //в случае ошибки - сигнализируем redux, что промис несложился
- }
- }
- // проверка работоспособности
- {
- // const store = createStore(promiseReducer)
- // store.subscribe(() => console.log(1, 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())))
- }
- }
- // authReducer
- {
- const jwtDecode = function (token) {
- try {
- let parseData = token.split('.')[1]
- return JSON.parse(atob(parseData))
- }
- catch (e) {
- return undefined
- }
- }
- function authReducer(state = {}, { type, token }) {
- if (type === 'AUTH_LOGIN') {
- let payload = jwtDecode(token)
- return state = {
- token,
- payload
- }
- }
- 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 store = createStore(authReducer)
- // store.subscribe(() => console.log(store.getState()))
- // store.dispatch(actionAuthLogin(token))
- /*{
- token: "eyJhbGc.....",
- payload: {
- "sub": {
- "id": "6377e133b74e1f5f2ec1c125",
- "login": "test5",
- "acl": [
- "6377e133b74e1f5f2ec1c125",
- "user"
- ]
- },
- "iat": 1668812458
- }
- }*/
- // store.dispatch(actionAuthLogout()) // {}
- }
- // cartReducer
- {
- function cartReducer(state = {}, { type, count, good }) {
- if (type === 'CART_ADD') {
- return {
- ...state,
- [good._id]: {
- good,
- count: (state[good._id] ? state[good._id].count + count : count)
- }
- }
- }
- if (type === 'CART_SUB') {
- if (state[good._id]) {
- let newCount = state[good._id].count - count
- if (newCount > 0) {
- return {
- ...state,
- [good._id]: {
- good,
- count: newCount
- }
- }
- } else {
- delete state[good._id]
- return { ...state }
- }
- } else {
- return undefined
- }
- }
- if (type === 'CART_DEL') {
- delete state[good._id]
- return { ...state }
- }
- if (type === 'CART_SET') {
- if (count > 0) {
- return {
- ...state,
- [good._id]: {
- good,
- count
- }
- }
- } else {
- delete state[good._id]
- return { ...state }
- }
- }
- if (type === 'CART_CLEAR') {
- return state = {}
- }
- return state
- }
- // экшоны:
- // Добавление товара.Должен добавлять новый ключ в state, или обновлять, если ключа в state ранее не было, увеличивая количество
- const actionCartAdd = (good, count = 1) => ({ type: 'CART_ADD', count, good })
- // Уменьшение количества товара.Должен уменьшать количество товара в state, или удалять его если количество будет 0 или отрицательным
- const actionCartSub = (good, count = 1) => ({ type: 'CART_SUB', count, good })
- // Удаление товара.Должен удалять ключ из state
- const actionCartDel = (good) => ({ type: 'CART_DEL', good })
- // Задание количества товара.В отличие от добавления и уменьшения, не учитывает того количества, которое уже было в корзине, а тупо назначает количество поверху(или создает новый ключ, если в корзине товара не было).Если count 0 или отрицательное число - удаляем ключ из корзины;
- const actionCartSet = (good, count = 1) => ({ type: 'CART_SET', count, good })
- // Очистка корзины.state должен стать пустым объектом { }
- const actionCartClear = () => ({ type: 'CART_CLEAR' })
- // Проверочный код
- const store = createStore(cartReducer)
- store.subscribe(() => console.log(store.getState())) //
- console.log(store.getState()) //{}
- store.dispatch(actionCartAdd({ _id: 'пиво', price: 50 }))
- // {пиво: {good: {_id: 'пиво', price: 50}, count: 1}}
- store.dispatch(actionCartAdd({ _id: 'чипсы', price: 75 }))
- // {
- // пиво: {good: {_id: 'пиво', price: 50}, count: 1},
- // чипсы: {good: {_id: 'чипсы', price: 75}, count: 1},
- //}
- store.dispatch(actionCartAdd({ _id: 'пиво', price: 50 }, 5))
- // {
- // пиво: {good: {_id: 'пиво', price: 50}, count: 6},
- // чипсы: {good: {_id: 'чипсы', price: 75}, count: 1},
- //}
- store.dispatch(actionCartSet({ _id: 'чипсы', price: 75 }, 2))
- // {
- // пиво: {good: {_id: 'пиво', price: 50}, count: 6},
- // чипсы: {good: {_id: 'чипсы', price: 75}, count: 2},
- //}
- store.dispatch(actionCartSub({ _id: 'пиво', price: 50 }, 4))
- // {
- // пиво: {good: {_id: 'пиво', price: 50}, count: 2},
- // чипсы: {good: {_id: 'чипсы', price: 75}, count: 2},
- //}
- store.dispatch(actionCartDel({ _id: 'чипсы', price: 75 }))
- // {
- // пиво: {good: {_id: 'пиво', price: 50}, count: 2},
- //}
- store.dispatch(actionCartClear()) // {}
- }
|