js.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. function createStore(reducer){
  2. let state = reducer(undefined, {}) //стартовая инициализация состояния, запуск редьюсера со state === undefined
  3. let cbs = [] //массив подписчиков
  4. const getState = () => state //функция, возвращающая переменную из замыкания
  5. const subscribe = cb => (cbs.push(cb), //запоминаем подписчиков в массиве
  6. () => cbs = cbs.filter(c => c !== cb)) //возвращаем функцию unsubscribe, которая удаляет подписчика из списка
  7. const dispatch = action => {
  8. if (typeof action === 'function'){ //если action - не объект, а функция
  9. return action(dispatch, getState) //запускаем эту функцию и даем ей dispatch и getState для работы
  10. }
  11. const newState = reducer(state, action) //пробуем запустить редьюсер
  12. if (newState !== state){ //проверяем, смог ли редьюсер обработать action
  13. state = newState //если смог, то обновляем state
  14. for (let cb of cbs) cb() //и запускаем подписчиков
  15. }
  16. }
  17. return {
  18. getState, //добавление функции getState в результирующий объект
  19. dispatch,
  20. subscribe //добавление subscribe в объект
  21. }
  22. }
  23. function promiseReducer(state = {}, {promiseName, type, status, payload, error}) {
  24. if (type === 'PROMISE') {
  25. return {...state, [promiseName]: {status, payload, error}}
  26. }
  27. return state
  28. }
  29. const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms));
  30. const actionPending = promiseName => ({promiseName, type: 'PROMISE', status: 'PENDING'})
  31. const actionFulfilled = (promiseName, payload) => ({promiseName, type: 'PROMISE', status: 'FULFILLED', payload})
  32. const actionRejected = (promiseName, error) => ({promiseName, type: 'PROMISE', status: 'REJECTED', error})
  33. const actionPromise = (promiseName, promise) =>
  34. async dispatch => {
  35. dispatch(actionPending(promiseName)) //сигнализируем redux, что промис начался
  36. try {
  37. const payload = await promise //ожидаем промиса
  38. dispatch(actionFulfilled(promiseName, payload)) //сигнализируем redux, что промис успешно выполнен
  39. return payload //в месте запуска store.dispatch с этим thunk можно так же получить результат промиса
  40. } catch (error) {
  41. dispatch(actionRejected(promiseName, error)) //в случае ошибки - сигнализируем redux, что промис несложился
  42. }
  43. }
  44. const store = createStore(promiseReducer)
  45. store.subscribe(() => console.log(store.getState())) //должен запускаться 6 раз
  46. store.dispatch(actionPromise('delay', delay(1000)))
  47. store.dispatch(actionPromise('luke', fetch("https://swapi.dev/api/people/1").then(res => res.json())))
  48. store.dispatch(actionPromise('tatooine', fetch("https://swapi.dev/api/planets/1").then(res => res.json())))
  49. /////////////////////////////////////////////////////////////////////
  50. function authReducer (state={}, {token, type}){
  51. if (type === 'AUTH_LOGIN') {
  52. try {
  53. let str = token.split('.')[1];
  54. let result = JSON.parse(atob(str))
  55. return {...state, 'token': token, 'playload':result}
  56. } catch (e) {
  57. return {}
  58. }
  59. }
  60. else if (type === 'AUTH_LOGOUT') {
  61. return {}
  62. }
  63. return state
  64. }
  65. const actionAuthLogin = token => ({type: 'AUTH_LOGIN', token})
  66. const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'})
  67. const token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2Mzc3ZTEzM2I3NGUxZjVmMmVjMWMxMjUiLCJsb2dpbiI6InRlc3Q1IiwiYWNsIjpbIjYzNzdlMTMzYjc0ZTFmNWYyZWMxYzEyNSIsInVzZXIiXX0sImlhdCI6MTY2ODgxMjQ1OH0.t1eQlRwkcP7v9JxUPMo3dcGKprH-uy8ujukNI7xE3A0"
  68. const storeAuth = createStore(authReducer)
  69. storeAuth.subscribe(() => console.log(storeAuth.getState()))
  70. storeAuth.dispatch(actionAuthLogin(token))
  71. // ///////////////////////////////////////////////////////////////////////
  72. function cartReducer (state = {}, {type, good, count}) {
  73. let goodKey, oldCount, goodValue;
  74. if (good) {
  75. goodKey = good['_id'];
  76. oldCount = state[goodKey]?.count || 0;
  77. goodValue = {good, count: oldCount}
  78. }
  79. if (type === 'CARD_ADD') {
  80. goodValue.count += count;
  81. return {...state, [goodKey]: goodValue}
  82. }
  83. else if (type === 'CART_DEL') {
  84. delete state[goodKey];
  85. }
  86. }
  87. const actionCartAdd = (good, count=1) => ({type: 'CART_ADD', count, good})
  88. const actionCartSub = (good, count=1) => ({type: 'CART_SUB', count, good})
  89. const actionCartDel = (good) => ({type: 'CART_DEL', good})
  90. const actionCartSet = (good, count=1) => ({type: 'CART_SET', count, good})
  91. const actionCartClear = () => ({type: 'CART_CLEAR'})
  92. /////////////////////////////////////////////////////////////////