Ivan Asmer 4 years ago
parent
commit
7ee537bee9
5 changed files with 172 additions and 79 deletions
  1. 86 0
      package-lock.json
  2. 1 0
      package.json
  3. 16 55
      src/App.js
  4. 2 11
      src/actions/index.js
  5. 67 13
      src/reducers/index.js

+ 86 - 0
package-lock.json

@@ -1179,6 +1179,63 @@
       "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz",
       "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw=="
     },
+    "@redux-saga/core": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.1.3.tgz",
+      "integrity": "sha512-8tInBftak8TPzE6X13ABmEtRJGjtK17w7VUs7qV17S8hCO5S3+aUTWZ/DBsBJPdE8Z5jOPwYALyvofgq1Ws+kg==",
+      "requires": {
+        "@babel/runtime": "^7.6.3",
+        "@redux-saga/deferred": "^1.1.2",
+        "@redux-saga/delay-p": "^1.1.2",
+        "@redux-saga/is": "^1.1.2",
+        "@redux-saga/symbols": "^1.1.2",
+        "@redux-saga/types": "^1.1.0",
+        "redux": "^4.0.4",
+        "typescript-tuple": "^2.2.1"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.7.7",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz",
+          "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==",
+          "requires": {
+            "regenerator-runtime": "^0.13.2"
+          }
+        }
+      }
+    },
+    "@redux-saga/deferred": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.1.2.tgz",
+      "integrity": "sha512-908rDLHFN2UUzt2jb4uOzj6afpjgJe3MjICaUNO3bvkV/kN/cNeI9PMr8BsFXB/MR8WTAZQq/PlTq8Kww3TBSQ=="
+    },
+    "@redux-saga/delay-p": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.1.2.tgz",
+      "integrity": "sha512-ojc+1IoC6OP65Ts5+ZHbEYdrohmIw1j9P7HS9MOJezqMYtCDgpkoqB5enAAZrNtnbSL6gVCWPHaoaTY5KeO0/g==",
+      "requires": {
+        "@redux-saga/symbols": "^1.1.2"
+      }
+    },
+    "@redux-saga/is": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.2.tgz",
+      "integrity": "sha512-OLbunKVsCVNTKEf2cH4TYyNbbPgvmZ52iaxBD4I1fTif4+MTXMa4/Z07L83zW/hTCXwpSZvXogqMqLfex2Tg6w==",
+      "requires": {
+        "@redux-saga/symbols": "^1.1.2",
+        "@redux-saga/types": "^1.1.0"
+      }
+    },
+    "@redux-saga/symbols": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.2.tgz",
+      "integrity": "sha512-EfdGnF423glv3uMwLsGAtE6bg+R9MdqlHEzExnfagXPrIiuxwr3bdiAwz3gi+PsrQ3yBlaBpfGLtDG8rf3LgQQ=="
+    },
+    "@redux-saga/types": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.1.0.tgz",
+      "integrity": "sha512-afmTuJrylUU/0OtqzaRkbyYFFNgCF73Bvel/sw90pvGrWIZ+vyoIJqA6eMSoA6+nb443kTmulmBtC9NerXboNg=="
+    },
     "@svgr/babel-plugin-add-jsx-attribute": {
       "version": "4.2.0",
       "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
@@ -10742,6 +10799,14 @@
         "symbol-observable": "^1.2.0"
       }
     },
+    "redux-saga": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.1.3.tgz",
+      "integrity": "sha512-RkSn/z0mwaSa5/xH/hQLo8gNf4tlvT18qXDNvedihLcfzh+jMchDgaariQoehCpgRltEm4zHKJyINEz6aqswTw==",
+      "requires": {
+        "@redux-saga/core": "^1.1.3"
+      }
+    },
     "redux-thunk": {
       "version": "2.3.0",
       "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz",
@@ -12475,6 +12540,27 @@
       "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
       "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
     },
+    "typescript-compare": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
+      "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==",
+      "requires": {
+        "typescript-logic": "^0.0.0"
+      }
+    },
+    "typescript-logic": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz",
+      "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q=="
+    },
+    "typescript-tuple": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz",
+      "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==",
+      "requires": {
+        "typescript-compare": "^0.0.2"
+      }
+    },
     "uglify-js": {
       "version": "3.4.10",
       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz",

+ 1 - 0
package.json

@@ -10,6 +10,7 @@
     "react-router-dom": "^5.1.2",
     "react-scripts": "3.2.0",
     "redux": "^4.0.4",
+    "redux-saga": "^1.1.3",
     "redux-thunk": "^2.3.0"
   },
   "scripts": {

+ 16 - 55
src/App.js

@@ -1,73 +1,34 @@
-import React  from 'react';
+import React, {useState }  from 'react';
 import logo from './logo.svg';
 import './App.css';
 import {Provider, connect}   from 'react-redux';
+import {actionSearch } from './actions';
 import store from './reducers'
-import { CounterView, Counter, ConnectedBIGTABLO, ConnectedCounter, ConnectedBigButton } from './components'
+//import { CounterView, Counter, ConnectedBIGTABLO, ConnectedCounter, ConnectedBigButton } from './components'
 import {Router, Route, Link, Switch, Redirect} from 'react-router-dom';
 import createHistory from "history/createBrowserHistory";
 
-const history = createHistory()
-
-setTimeout(() => history.push('/counters/100/300'), 5000)
-
-const PageCounters= ({match: {params: {a, b}}}) =>
-    <>
-        <ConnectedBigButton />
-        {console.log(a, b)}
-                <CounterView value={a} />
-                {+a + +b}
-                <Counter />
-                <Counter />
-                <Counter />
-                <ConnectedBIGTABLO />
-                <ConnectedCounter />
-                <ConnectedCounter />
-                <ConnectedCounter />
-                <ConnectedCounter />
-                <ConnectedCounter />
-                <ConnectedCounter />
-    </>
-
-const Header = connect(st => ({auth: {}}))(({auth}) => 
-<header>
-    <Link to="/">D2</Link>
-    <Link to="/counters/5/2">S4et4icks (5, 2)</Link>
-    <Link to="/counters/7/12">S4et4icks (7, 12)</Link>
-    {!auth.decoded && <Link to="/login">Login</Link>}
-    {auth.decoded && <Link to="/logout">Logout</Link>}
-    {auth.decoded && <Link to="/new">New...</Link>}
-</header>)
-
-export const Menu = ({}) => <aside style={{float: 'left', width: '33%'}}>Aside</aside>
-
-const PageMain = () => <h1>Main</h1>
-const Page404 = () => <h1>404 not found</h1>
-
-export const Content = ({}) => 
-<content style={{float: 'left',width: '66%' }}>
-    <Switch>
-        <Route path="/" component={PageMain} exact />
-        <Route path="/counters/:a/:b" component={PageCounters} exact />
-        <Route component={Page404} />
-    </Switch>
-</content>
-
-export const Footer = ({}) => <footer>Footer</footer>
 
+const history = createHistory()
 
+const SearchField = connect(null, {onChange: actionSearch})(({onChange}) => {
+    const [text, setText] = useState('')
+    return (
+        <input value={text} onChange={e => (setText(e.target.value), onChange(e.target.value))}/>
+    )
+})
 
+const SearchResult = connect(s => (console.log(s), ({payload: s.searchResult && 
+                                                              s.searchResult.payload && 
+                                                              s.searchResult.payload.payload && 
+                                                              s.searchResult.payload.payload.GoodFind})))(({payload}) => <h1>{console.log(payload)}{payload && payload.map(({_id, name}) => <div>{name}</div>)}</h1>)
 
 export default () => {
-    //setTimeout(() => setA(a +1), 2000)
     return (
         <Provider store={store}>
+            <SearchField />
+            <SearchResult />
             <Router history={history}>
-        <Redirect from="/главная" to="/"/>
-                <Header />
-                <Menu />
-                <Content />
-                <Footer />
             </Router>
         </Provider>
     )

+ 2 - 11
src/actions/index.js

@@ -1,11 +1,2 @@
-export const actionInc = () => ({type: 'COUNTER_INC'})
-
-const delay = ms => new Promise(ok => setTimeout(ok, ms))
-
-export const actionPlus10 = () => async dispatch => {
-    for (var i=0;i<10;i++){
-        await delay(500)
-        dispatch(actionInc())
-    }
-}
-export const actionDec = () => ({type: 'COUNTER_DEC'})
+export const actionSearch = text => ({type: 'SEARCH', text})
+export const actionSearchResult = payload => ({type: 'SEARCH_RESULT', payload})

+ 67 - 13
src/reducers/index.js

@@ -1,21 +1,75 @@
 import {createStore, applyMiddleware, combineReducers} from 'redux';
-import thunk from 'redux-thunk';
+//import thunk from 'redux-thunk';
+import createSagaMiddleware from  'redux-saga';
+import { all, takeLatest, takeLeading, takeEvery, put } from 'redux-saga/effects';
+import {actionSearchResult} from '../actions';
+import { GraphQLClient } from 'graphql-request';
 
+const gql = new GraphQLClient('http://shop-roles.asmer.fs.a-level.com.ua/graphql')
 
-let store = createStore((state, action) => { //единственный редьюсер данного хранилища
-    if (state === undefined){ //redux запускает редьюсер хотя бы раз, что бы инициализировать хранилище
-        return {counter: 0};  //обязательно вернуть новый объект, а не изменить текущий state
-    }
-    if (action.type === 'COUNTER_INC'){ //в каждом action должен быть type
-        return {counter: state.counter +1} //создаем новый объект базируясь на данных из предыдущего состояния
-    }
-    if (action.type === 'COUNTER_DEC'){
-        return {counter: state.counter -1}
+const sagaMiddleware = createSagaMiddleware()
+
+
+let store = createStore((state={}, {type, ...params}) => { //единственный редьюсер данного хранилища
+    if (type === 'SEARCH_RESULT'){
+        return {searchResult: {...params}}
     }
-    return state; //редьюсеров может быть несколько, в таком случае вызываются все редьюсеры, но далеко не всегда action.type будет относится к этому редьюсеру. Тогда редьюсер должен вернуть state как есть. 
-}, applyMiddleware(thunk))
+    return state//, в таком случае вызываются все редьюсеры, но далеко не всегда action.type будет относится к этому редьюсеру. Тогда редьюсер должен вернуть state как есть. 
+}, applyMiddleware(sagaMiddleware))
+
+const delay = ms => new Promise(ok => setTimeout(() => ok(ms), ms))
+
+function* aWorker({timeout, message}){
+    yield put({type: 'LOG', text: `aWorker started with ${timeout}`})
+    yield delay(timeout)
+    console.log(message)
+}
+
+function* actionCheck(){
+    yield takeLeading('ACTION_A', aWorker)
+}
+
+function* searchWorker({text}){
+    yield put(actionSearchResult({payload: null}))
+    yield delay(2000)
+//    let payload = yield delay(2000) //типа fetch
+    let payload = yield gql.request(`
+            query gf($query: String){
+                GoodFind(query: $query){
+                    _id, name, description, price, images{
+                        _id, url
+                    }
+                }
+            }`, {query: JSON.stringify([
+                        {
+                            $or: [{name: `/${text}/`}, {description: `/${text}/`}] //регулярки пишутся в строках
+                        },
+                        {
+                            sort: [{name: 1}]} //сортируем по title алфавитно
+                        ])
+            }) 
+    yield put(actionSearchResult({payload}))
+    console.log('search end' , text)
+}
+
+function* searchCheck(){
+    yield takeLatest('SEARCH', searchWorker)
+}
+
+
+function* rootSaga(){
+    yield all([
+        actionCheck(),
+        searchCheck()
+    ])
+}
+
+sagaMiddleware.run(rootSaga)
+
+//store.subscribe(()=> console.log(store.getState())) // подписка на обновления store
 
-store.subscribe(()=> console.log(store.getState())) // подписка на обновления store
+store.dispatch({type: 'ACTION_A', timeout: 5000, message: 'HELLO WORLD 5sec'})
+store.dispatch({type: 'ACTION_A', timeout: 2000, message: 'WORLD HELLO 2sec'})
 
 export default store