Quellcode durchsuchen

thunk pre alpha

Ivan Asmer vor 5 Jahren
Ursprung
Commit
6d8a1da136
2 geänderte Dateien mit 127 neuen und 4 gelöschten Zeilen
  1. 4 4
      reduxHW.md
  2. 123 0
      thunk.md

+ 4 - 4
reduxHW.md

@@ -98,7 +98,7 @@ Chat = connect(mapStateToProps)(Chat) //Chat component override by redux compone
 store.subscribe(() => console.log(store.getState()))
 ```
 
-расскажет вам всё, что твориться в хранилище.
+расскажет вам всё, что творится в хранилище.
 
 ### `connect`
 
@@ -107,7 +107,7 @@ store.subscribe(() => console.log(store.getState()))
 ```jsx
 ...
 render(){
-    console.log(this.props)
+    console.log('<Component /> Props: ',this.props)
     return (
         ....
     )
@@ -115,9 +115,9 @@ render(){
 ...
 ```
 
-Если у вас там все правильно, то вы увидите ключи из объекта, возвращаемого `mapStateToProps` и названия **actionCreator**(ов) из `mapDispatchToProps`.
+Если у вас там все правильно, то вы увидите ключи из объекта, возвращаемого `mapStateToProps` и названия **actionCreator**(-ов) из `mapDispatchToProps`.
 
 ### `props`
 
-И наоборот, вы всегда можете просимулировать **Redux**, если явно пропишите исходному компоненту параметры через `props` вместо использования `connect` 
+И наоборот, вы всегда можете просимулировать **Redux**, если явно пропишите исходному компоненту параметры через `props` (используя "атрибуты тэга" JSX) вместо использования `connect` 
 и хранилища.

+ 123 - 0
thunk.md

@@ -0,0 +1,123 @@
+# React-Thunk
+
+## Поток данных.
+
+В целом в **React-Redux** поток данных несложен:
+
+- Компонент через `connect` присоединяется через `props` к **redux**, получая `actionCreator`-ы для отправки в **redux**, и данные из хранилища.
+- При выполнении диспетчеризации `action` меняется состояние хралища.
+- Новое состояние автоматически обновляет все подписанные через `connect` компоненты.
+
+В этом цикле не совсем ясно, куда воткнуть обращения к бэку, при условии, что:
+
+- Компонент должен остаться независимым и связанным с внешним миром только через `connect`,
+- **Redux** - хранилище синхронное, а получение информации с бэка - процесс многоэтапный и асинхронный.
+- Компоненты должны знать о состоянии процесса отправки или получения данных на бэк (состоянии промиса, как минимум).
+
+В таком случае слой отправки-получения данных можно встроить в два места:
+- В `actionCreator`, __перед__ диспетчеризацией.
+- В том или ином слушателе состояния **redux** (используя `subscribe`), внутри хранилища создается специальная ветвь для хранения информации о желании компонентов получить что-то с **back-end**.
+
+Первый подход более прост и распространен. 
+
+## actionCreator
+
+В коде **actionCreator** должны быть реализованы асинхронные обращения. Если разобрать жизненный путь `Promise`, то обычный actionCreator будет представлять что-то вроде:
+
+```javascript
+//три состояния
+const actionPending     = () => ({ type: 'SET_STATUS', status: 'PENDING', payload: null })
+const actionResolved    = payload => ({ type: 'SET_STATUS', status: 'RESOLVED', payload })
+const actionRejected    = error => ({ type: 'SET_STATUS', status: 'REJECTED', payload: null, error })
+
+//асинхронный многоэтапный экшен
+const actionFetch   = () => {
+    let promise = fetch("...")
+    promise.then(
+        data => store.dispatch(actionResolved(data)),
+        error => store.dispatch(actionRejected(error))
+    )
+
+    return actionPending()
+}
+```
+
+Таким образом синхронная функция `actionFetch` обращается куда надо на сервер, навешивает обработчики результатов промиса в `then`, и возвращает
+изначальное состояние `actionPending`.
+
+Когда промис поменяет свое состояние, это будет обработано **redux** через действия `actionResolved` и `actionRejected`. 
+
+## Редьюсер
+
+Где-то в недрах редьюсеров:
+
+```javascript
+    ...
+        
+    if (action.type === 'SET_STATUS'){
+        return {status: action.status, payload: action.payload}
+    }
+
+    ...
+```
+
+Что в этом подходе неудобно, так это привязка к `store`, и то, что `actionFetch` "мимикрирует" под `actionPending`. 
+
+Встречайте, `redux-thunk`
+
+## Redux-thunk
+
+**Redux-thunk** предоставляет возможность передавать вместо *действия-объекта* *действие-функцию*. Теперь **actionCreator** 
+может возвращать функции, и `dispatch` из запустит. Сама же функция может реализовывать любой длительный асинхронный
+процесс, запуская `dispatch` для изменения состояния **Redux**.
+
+```bash
+npm install --save redux-thunk
+```
+
+```javascript
+import thunk from 'redux-thunk';
+
+...
+
+const store = createStore(msgStatusReducer, applyMiddleware(thunk)) //вторым параметром идет миддлварь
+```
+
+Теперь функция `actionFetch` выглядит немного по-другому:
+
+```javascript
+const actionFetch   = () => {
+    return dispatch => { //возвращаем функцию. 
+        let promise = fetch("...")
+        dispatch(actionPending())
+        promise.then(
+            data => dispatch(actionResolved(data)),
+            error => dispatch(actionRejected(error))
+        )
+    }
+}
+```
+
+- **actionCreator** возвращает не *объект*, а *функцию*;
+- **Redux** её запускает, передавая свой `dispatch`;
+- *функция* инициирует асинхронный процесс и использует простые объектные **actionCreator** для оповещении хранилища;
+
+## `async function` тоже можно:
+
+```javascript
+function actionFetch(){
+    return async function (dispatch){
+        dispatch(actionPending())
+        try {
+            dispatch(actionResolved(await fetch(...)))
+        }
+        catch (e) {
+            dispatch(actionRejected(e))
+        }
+    }
+}
+```
+
+Подход с использованием `async function` предпочтителен, так как он позволяет линейно описать асинхронный процесс любой сложности. Подобным образом работает **Redux-Saga**.
+И если на простых примерах синхронный подход с `then` мало отличается от асинхронной функции; то при нескольких обращениях зависящих друг от друга, код асинхронной
+функции будет намного прозрачнее.