浏览代码

redux-thunk-CW

ivar_n 2 年之前
父节点
当前提交
148aaab1b0
共有 3 个文件被更改,包括 261 次插入153 次删除
  1. 1 1
      js/15_graph-ql/index.js
  2. 14 2
      js/16_redux-thunk/index.html
  3. 246 150
      js/16_redux-thunk/index.js

+ 1 - 1
js/15_graph-ql/index.js

@@ -47,7 +47,7 @@
 
    const originalFetch = fetch;
    fetch = (url, params={headers:{}}) => { 
-      if (localStorage.authToken !== undefined) {
+      if (localStorage.authToken !== 'undefined') {
          params.headers.Authorization = "Bearer " + localStorage.authToken
       }
       return originalFetch(url, params)

+ 14 - 2
js/16_redux-thunk/index.html

@@ -12,12 +12,16 @@
       #aside {
           width: 30%;
       }
-      #aside > a{
+      #aside > a {
           display: block;
       }
-      #main > a{
+      #main > a {
           display: block;
       }
+      #loginContainer  {
+         display: flex;
+         flex-direction: column;
+      }
   </style>
 </head>
    <body>
@@ -30,6 +34,14 @@
               Контент
           </main>
       </div>
+
+        <div id="loginContainer">
+            <input id="loginInput" type="text"/>
+            <input id="passInput" type="password"/>
+            <button id="loginBtn">LOGIN</button>
+            <button id="logoutBtn">LOGOUT</button>
+        </div>
+
       <script src='index.js'></script>
   </body>
 

+ 246 - 150
js/16_redux-thunk/index.js

@@ -25,16 +25,101 @@ function createStore(reducer){
     }
 }
 
+function combineReducers(reducers) {
+    return (state={}, action) => {
+        const newState = {}
+        // перебрать все редьюсеры
+        if (reducers) {
+            for (const [reducerName, reducer] of Object.entries(reducers)) {
+                const newSubState = reducer(state[reducerName], action)
+                if (newSubState !== state[reducerName]) {
+                    newState[reducerName] = newSubState
+                }
+            }
+            // если newState не пустой, то вернуть стейт в 
+            if (Object.keys(newState).length !== 0) {
+                return {...state, ...newState}
+            } else {
+                return state
+            }
+        }
 
+    }
+}
+const combinedReducer = combineReducers({promise: promiseReducer, auth: authReducer})
+const store = createStore(combinedReducer)
+// console.log(store.getState()) // {promise: {}, auth: {}}
 
-function promiseReducer(state={}, {type, status, payload, error, name}) {
-    if (!state) { 
+
+
+
+function jwtDecode(token) {
+    try {
+        let decoded = JSON.parse(atob(token.split('.')[1])) 
+        return decoded
+    } catch (err) {
+        console.log(err)
+    }
+}
+
+function authReducer(state, {type, token}) {
+    if (!state) {
+        if (localStorage.authToken) {
+            token = localStorage.authToken
+            type = 'AUTH_LOGIN'
+        } else {
+            return {}
+        }
+    }
+    if (type === 'AUTH_LOGIN') {
+        let payload = jwtDecode(token)
+        if (typeof payload === 'object') {
+            localStorage.authToken = token
+            return {
+                ...state,
+                token, 
+                payload
+            }
+        } else {
+            return state
+        }
+    }
+    if (type === 'AUTH_LOGOUT') {
+        delete localStorage.authToken
         return {}
-            //{ login: {status, payload, error},
-            //  catById: {status, payload, error}
-            //}
     }
+    return state
+}
+
+const actionAuthLogin = (token) => ({type: 'AUTH_LOGIN', token})
+const actionAuthLogout = () => ({type: 'AUTH_LOGOUT'})
+
+// const loginStore = createStore(authReducer)
+store.subscribe(() => console.log(store.getState()))
+
+
+const inputToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOnsiaWQiOiI2MWE0ZGIyOWM3NTBjMTJiYTZiYTQwMjIiLCJsb2dpbiI6ImVxd2VxZXdldyIsImFjbCI6WyI2MWE0ZGIyOWM3NTBjMTJiYTZiYTQwMjIiLCJ1c2VyIl19LCJpYXQiOjE2MzgxOTQ1NzZ9.Pi1GO6x7wdNrIrUKCQT-32-SsqmgFY-oFDrrXmw74-8'
+
+// loginStore.dispatch(actionAuthLogin(inputToken))
+// loginStore.dispatch(actionAuthLogout())
+// console.log(store.getState())
+
+loginBtn.onclick = () => {
+    store.dispatch(actionAuthLogin(inputToken))
+}
 
+logoutBtn.onclick = () => {
+    store.dispatch(actionAuthLogout())
+}
+
+
+
+
+
+function promiseReducer(state={}, {type, status, payload, error, name}) {
+    if (!state) {
+        return {}
+    }
     if (type === 'PROMISE') {         
         return {
             ...state,
@@ -52,33 +137,33 @@ const actionPending = (name) => ({type: 'PROMISE', status: 'PENDING', name})
 const actionResolved = (name, payload) => ({type: 'PROMISE', status: 'RESOLVED', name, payload})
 const actionRejected = (name, error) => ({type: 'PROMISE', status: 'REJECTED', name, error})
 
-const store = createStore(promiseReducer)
-store.subscribe(() => console.log(store.getState()))
+// const promiceStore = createStore(promiseReducer)
+// store.subscribe(() => console.log(store.getState()))
 
 
-// const delay = (ms) => new Promise((ok) => setTimeout(() => ok(ms), ms))
+const delay = (ms) => new Promise((ok) => setTimeout(() => ok(ms), ms))
 
-// store.dispatch(actionPending('delay1000'))
-// delay(1000).then(data => store.dispatch(actionResolved('delay1000', data)),
-//                  error => store.dispatch(actionRejected('delay1000', error)))
+// // store.dispatch(actionPending('delay1000'))
+// // delay(1000).then(data => store.dispatch(actionResolved('delay1000', data)),
+// //                  error => store.dispatch(actionRejected('delay1000', error)))
 
-// store.dispatch(actionPending('delay2000'))
-// delay(2000).then(data => store.dispatch(actionResolved('delay2000', data)),
-//                  error => store.dispatch(actionRejected('delay2000', error)))
+// // store.dispatch(actionPending('delay2000'))
+// // delay(2000).then(data => store.dispatch(actionResolved('delay2000', data)),
+// //                  error => store.dispatch(actionRejected('delay2000', error)))
 
 
 const actionPromise = (name, promise) => 
-async dispatch => {
-    dispatch(actionPending(name))
-    try { 
-        let data = await promise
-        dispatch(actionResolved(name, data))
-        return data
-    }
-    catch(error){
-        dispatch(actionRejected(name, error))
+    async dispatch => {
+        dispatch(actionPending(name))
+        try { 
+            let data = await promise
+            dispatch(actionResolved(name, data))
+            return data
+        }
+        catch(error){
+            dispatch(actionRejected(name, error))
+        }
     }
-}
 
 const getGQL = url =>
   async (query, variables={}) => {
@@ -107,141 +192,152 @@ const getGQL = url =>
   const gql = getGQL(backURL + 'graphql');
 
 
-  const actionRootCats = () => 
-  actionPromise('rootCats', gql(`query {
-      CategoryFind(query: "[{\\"parent\\":null}]"){
-          _id name
-      }
-  }`))
-
-const actionCatById = (_id) =>  //добавить подкатегории
-  actionPromise('catById', gql(`query catById($q: String){
-      CategoryFindOne(query: $q){
-          _id name goods {
-              _id name price images {
-                  url
-              }
-          }
-          subCategories {
-              _id name 
-          }
-      }
-  }`, {q: JSON.stringify([{_id}])}))
-
-//actionGoodById по аналогии
-
-const actionGoodById = (_id) => 
-  actionPromise('goodById', gql(`query goodById($q: String) {
-      GoodFindOne(query: $q) {
-          _id name price description images {
-            url
-          }
-        }
-  }`, {q: JSON.stringify([{_id}])}))
+//   const actionRootCats = () => 
+//   actionPromise('rootCats', gql(`query {
+//       CategoryFind(query: "[{\\"parent\\":null}]"){
+//           _id name
+//       }
+//   }`))
+
+// const actionCatById = (_id) =>  //добавить подкатегории
+//   actionPromise('catById', gql(`query catById($q: String){
+//       CategoryFindOne(query: $q){
+//           _id name goods {
+//               _id name price images {
+//                   url
+//               }
+//           }
+//           subCategories {
+//               _id name 
+//           }
+//       }
+//   }`, {q: JSON.stringify([{_id}])}))
+
+// //actionGoodById по аналогии
+
+// const actionGoodById = (_id) => 
+//   actionPromise('goodById', gql(`query goodById($q: String) {
+//       GoodFindOne(query: $q) {
+//           _id name price description images {
+//             url
+//           }
+//         }
+//   }`, {q: JSON.stringify([{_id}])}))
 
 
   
-store.dispatch(actionRootCats())
-
-store.subscribe(() => {
-    const {rootCats} = store.getState()
-    if (rootCats?.payload) {
-        aside.innerHTML = ''
-        for (const {_id, name} of rootCats?.payload) {
-            const link = document.createElement('a')
-            link.href = `#/category/${_id}`
-            link.innerText = name
-            aside.append(link)
-        }
-    }
-})
-
-
-// location.hash - адресная строка после решетки
-window.onhashchange = () => {
-    const [,route, _id] = location.hash.split('/')
-
-    const routes = {
-        category(){
-            store.dispatch(actionCatById(_id))
-            console.log('страница категорий')           
-        },
-        good(){ //задиспатчить actionGoodById
-            store.dispatch(actionGoodById(_id))
-            console.log('страница товара')                
-        },
-    }
-    if (route in routes)
-        routes[route]()
-}
-
-window.onhashchange()
-
-store.subscribe(() => {
-    const {catById} = store.getState()
-    const [,route, _id] = location.hash.split('/')
-    if (catById?.payload && route === 'category'){
-        const {name} = catById.payload;
-        main.innerHTML =    `<h1>${name}</h1>`
+// store.dispatch(actionRootCats())
+
+// store.subscribe(() => {
+//     const {rootCats} = store.getState()
+//     if (rootCats?.payload) {
+//         aside.innerHTML = ''
+//         for (const {_id, name} of rootCats?.payload) {
+//             const link = document.createElement('a')
+//             link.href = `#/category/${_id}`
+//             link.innerText = name
+//             aside.append(link)
+//         }
+//     }
+// })
+
+
+// // location.hash - адресная строка после решетки
+// window.onhashchange = () => {
+//     const [,route, _id] = location.hash.split('/')
+
+//     const routes = {
+//         category(){
+//             store.dispatch(actionCatById(_id))
+//             console.log('страница категорий')           
+//         },
+//         good(){ //задиспатчить actionGoodById
+//             store.dispatch(actionGoodById(_id))
+//             console.log('страница товара')                
+//         },
+//     }
+//     if (route in routes)
+//         routes[route]()
+// }
+
+// window.onhashchange()
+
+// store.subscribe(() => {
+//     const {catById} = store.getState()
+//     const [,route, _id] = location.hash.split('/')
+//     if (catById?.payload && route === 'category'){
+//         const {name} = catById.payload;
+//         main.innerHTML =    `<h1>${name}</h1>`
       
-        if (catById.payload.subCategories) {
-            for(const {_id, name} of catById.payload.subCategories) {
-                const link      = document.createElement('a');
-                link.href       = `#/category/${_id}`;
-                link.innerText  = name;
-                main.append(link);
-            }
-        }
+//         if (catById.payload.subCategories) {
+//             for(const {_id, name} of catById.payload.subCategories) {
+//                 const link      = document.createElement('a');
+//                 link.href       = `#/category/${_id}`;
+//                 link.innerText  = name;
+//                 main.append(link);
+//             }
+//         }
         
-        if (catById.payload.goods) {
-            for (const {_id, name, price, images} of catById.payload.goods){
-                const card      = document.createElement('div')
-                card.innerHTML = `<h2>${name}</h2>
-                                  <img src="${backURL}/${images[0].url}" />
-                                  <strong>${price}</strong>
-                                  <br>
-                                  <a href="#/good/${_id}">Перейти на страницу товара</a>
-                                    `
-                main.append(card)
-            }
-        }
-    }
-})
-
-store.subscribe(() => {
-    const {goodById} = store.getState();
-    const [,route, _id] = location.hash.split('/');
-    if (goodById?.payload && route === 'good') {
-        main.innerHTML = '';
-        const {_id, name, images, price, description}  = goodById.payload;
-            const card = document.createElement('div');
-            card.innerHTML = `<h2>${name}</h2>
-                            <img src="${backURL}/${images[0].url}" />
-                            <strong>${price}</strong>
-                            <h2>${description}</h2>
-                            `;
-            main.append(card);
+//         if (catById.payload.goods) {
+//             for (const {_id, name, price, images} of catById.payload.goods){
+//                 const card      = document.createElement('div')
+//                 card.innerHTML = `<h2>${name}</h2>
+//                                   <img src="${backURL}/${images[0].url}" />
+//                                   <strong>${price}</strong>
+//                                   <br>
+//                                   <a href="#/good/${_id}">Перейти на страницу товара</a>
+//                                     `
+//                 main.append(card)
+//             }
+//         }
+//     }
+// })
+
+// store.subscribe(() => {
+//     const {goodById} = store.getState();
+//     const [,route, _id] = location.hash.split('/');
+//     if (goodById?.payload && route === 'good') {
+//         main.innerHTML = '';
+//         const {_id, name, images, price, description}  = goodById.payload;
+//             const card = document.createElement('div');
+//             card.innerHTML = `<h2>${name}</h2>
+//                             <img src="${backURL}/${images[0].url}" />
+//                             <strong>${price}</strong>
+//                             <h2>${description}</h2>
+//                             `;
+//             main.append(card);
+//         }
+//     }
+//     //ТУТ ДОЛЖНА БЫТЬ ПРОВЕРКА НА НАЛИЧИЕ goodById в редакс
+//     //и проверка на то, что сейчас в адресной строке адрес ВИДА #/good/АЙДИ
+//     //в таком случае очищаем main и рисуем информацию про товар с подробностями
+// )
+
+store.dispatch(actionPromise('delay1000', delay(1000)))
+// // store.dispatch(actionPromise('delay2000', delay(2000)))     
+// // store.dispatch(actionPromise('luke', fetch('https://swapi.dev/api/people/1/').then(res => res.json())))
+
+
+
+
+
+
+
+const actionLogin = (login, password) => (
+    actionPromise('catById', gql(`query log($login: String, $password: String) {
+        login(login: $login, password: $password)
+      }`, {login, password}))
+)
+
+const actionFullLogin = (login, password) => (
+    async dispatch => {
+        let token = await dispatch(actionLogin(login, password))
+        if (token) {
+            dispatch(actionAuthLogin(token))
         }
     }
-    //ТУТ ДОЛЖНА БЫТЬ ПРОВЕРКА НА НАЛИЧИЕ goodById в редакс
-    //и проверка на то, что сейчас в адресной строке адрес ВИДА #/good/АЙДИ
-    //в таком случае очищаем main и рисуем информацию про товар с подробностями
 )
 
-// store.dispatch(actionPromise('delay1000', delay(1000)))
-// store.dispatch(actionPromise('delay2000', delay(2000)))     
-// store.dispatch(actionPromise('luke', fetch('https://swapi.dev/api/people/1/').then(res => res.json())))
-
-
-
-
-
-
-
-
-
-
-