Статья, резюмирующая Redux, React-Redux, Redux-Saga.

React.js
Не хочется просыпаться, предпочитаю все время предаваться снисходительности. Я не знаю пути назад, Я лучше буду гнаться вечно без сожалений. --- Ван Сяобо

В этой статье в основном извлекается основное содержание продуктов бочек семейства реактивов, а сущность сравнима с эфирными маслами. Дамы и господа, раз уж вы здесь, присаживайтесь, гость, подойдите и покажите гостю чай~~

redux

предисловие

Прежде всего, эта статья требует от вас определенного понимания js, react и др. Если вы этого не знаете, я предлагаю вам сначала взглянуть на это:Суть React! Полное резюме (быстро)

В React есть реквизиты и состояние:

  1. props означает свойства, распространяемые родителем
  2. Состояние означает состояние, которым компонент может управлять сам по себе, и весь React не имеет возможности возвращать данные, что является односторонним потоком данных реакции.

Это означает, что если это приложение с очень сложным состоянием данных, оно будет найдено чаще.React просто не может заставить два компонента общаться друг с другом, используя данные другой стороны, метод реакции реакции на передачу данных через слои очень неудобен, в настоящее время срочно нужен механизм.Сконцентрируйте все состояния в верхней части компонента и гибко распределяйте все состояния по всем компонентам по мере необходимости., да это редукс

Введение

  1. Redux был создан, чтобы обеспечить механизм «предсказуемого управления состоянием» для приложений React.
  2. Redux хранит все состояние приложения (фактически данные) в одном месте, называемом хранилищем
  3. В этом хранилище хранится дерево состояний (state tree)
  4. Единственный способ для компонента изменить состояние — вызвать действие, вызвав метод отправки хранилища, это действие обрабатывается соответствующим редьюсером, и состояние обновляется.
  5. Компоненты могут отправлять действия в хранилище вместо того, чтобы уведомлять другие компоненты напрямую.
  6. Другие компоненты могут обновлять свои представления, подписываясь на состояние в магазине.

Шаги для использования

  1. создать редуктор
    • Вы можете использовать один редуктор или объединить несколько редьюсеров в один, то есть:combineReducers()
    • После того, как действие выдаст команду, поместите состояние в функцию обработки reucer, верните новое состояние и обработайте состояние.
  2. создать действие
    • Пользователь не может коснуться состояния и может быть вызван только представлением, поэтому это действие можно понимать как команду, а команд столько же, сколько действий, которые необходимо выполнить.
    • action — это объект, который должен иметь параметр с именем type, который определяет тип действия
  3. Созданный магазин, используйте метод createStore
    • магазин можно понимать как общую фабрику с несколькими обрабатывающими машинами
    • Предоставляет такие методы, как подписка, отправка и getState.

Следуйте инструкциям, чтобы сражаться на практике.

Для вышеуказанных шагов, соответствующий серийный номер, я отмечу его в соответствующем коде

npm install redux -S // 安装

import { createStore } from 'redux' // 引入

const reducer = (state = {count: 0}, action) => {----------> ⑴
  switch (action.type){
    case 'INCREASE': return {count: state.count + 1};
    case 'DECREASE': return {count: state.count - 1};
    default: return state;
  }
}

const actions = {---------->⑵
  increase: () => ({type: 'INCREASE'}),
  decrease: () => ({type: 'DECREASE'})
}

const store = createStore(reducer);---------->⑶

store.subscribe(() =>
  console.log(store.getState())
);

store.dispatch(actions.increase()) // {count: 1}
store.dispatch(actions.increase()) // {count: 2}
store.dispatch(actions.increase()) // {count: 3}

Я сам нарисовал очень простую блок-схему, чтобы облегчить понимание рабочего процесса редукции.

react-redux

Как я сказал в начале, если хранилище напрямую интегрировано в реквизиты верхнего уровня приложения React, при условии, что каждый подкомпонент может получить доступ к реквизитам верхнего уровня, например:

<顶层组件 store={store}>
  <App />
</顶层组件>

Разве это не нормально? Это реакция-редукс. Официальная библиотека привязки React, предоставленная Redux. Эффективный и гибкий.

React Redux разделяет компоненты на компоненты-контейнеры и компоненты пользовательского интерфейса.

  1. Первый будет обрабатывать логику
  2. Последний отвечает только за отображение и взаимодействие, никакая логика не обрабатывается внутри, а состояние полностью контролируется извне.

два ядра

  • Provider

    Посмотрите на код вышекомпонент верхнего уровня4 слова. Да, вы угадали. Этот компонент верхнего уровня является поставщиком.Как правило, мы оборачиваем компонент верхнего уровня в компонент поставщика, чтобы все компоненты могли находиться под контролем react-redux.Но магазин должен быть размещен в компоненте Provider в качестве параметра

    <Provider store = {store}>
        <App />
    <Provider>
    
    这个组件的目的是让所有组件都能够访问到Redux中的数据。 
    
  • connect

    Это самая сложная часть react-redux, давайте объясним это подробно

    Во-первых, запомните следующую строку кода:

    connect(mapStateToProps, mapDispatchToProps)(MyComponent)
    

    mapStateToProps

    Это слово переводится какСопоставить состояние с реквизитом, на самом деле этоСопоставьте данные в Redux с реквизитами в React.

    Возьмите каштан:

    const mapStateToProps = (state) => {
      return {
      	// prop : state.xxx  | 意思是将state中的某个数据映射到props中
        foo: state.bar
      }
    }

Затем вы можете использовать this.props.foo при рендеринге.

class Foo extends Component {
    constructor(props){
        super(props);
    }
    render(){
        return(
        	// 这样子渲染的其实就是state.bar的数据了
            <div>this.props.foo</div>
        )
    }
}
Foo = connect()(Foo);
export default Foo;

Затем вы можете завершить рендеринг

mapDispatchToProps

Это слово переводится какПревратите различные рассылки в реквизит, чтобы вы могли использовать их напрямую

const mapDispatchToProps = (dispatch) => { // 默认传递参数就是dispatch
  return {
    onClick: () => {
      dispatch({
        type: 'increatment'
      });
    }
  };
}
class Foo extends Component {
    constructor(props){
        super(props);
    }
    render(){
        return(
        	
             <button onClick = {this.props.onClick}>点击increase</button>
        )
    }
}
Foo = connect()(Foo);
export default Foo;

Компонент также изменен на указанный выше, вы можете вызвать диспетчеризацию напрямую через this.props.onClick, поэтому вам не нужно выполнять store.dispatch в коде.

Основное введение в реакцию-редукс здесь

redux-saga

Согласно исходному рабочему процессу редукции, когда в компоненте генерируется действие, редуктор будет напрямую запускаться для изменения состояния, а редуктор — это чистая функция, то есть в редюсере нельзя выполнять асинхронные операции;

На практике после того, как в компоненте происходит действие, перед входом в редьюсер нужно выполнить асинхронную задачу, например, после отправки ajax-запроса и получения данных, затем вход в редьюсер, очевидно, нативный редукс этого не поддерживает. вид операции.

В настоящее время для работы с этим бизнес-сценарием срочно требуется промежуточное программное обеспечение.Самый элегантный способ справиться с этим, естественно, Redux-saga.

Основное объяснение

1. Вспомогательная функция Саги

redux-saga предоставляет некоторые вспомогательные функции для получения задач, когда в магазине инициируются определенные действия.Позвольте мне сначала объяснить две вспомогательные функции:takeEveryа такжеtakeLatest

  • takeEvery

takeEvery похож на посудомоечную машину на сборочной линии. Когда приходит грязная тарелка, она непосредственно выполняет последующую функцию мытья. Как только вы наняли посудомоечную машину, он всегда будет выполнять эту работу и никогда не остановит процесс контроля сбора посуды. посуду и запустить процесс мытья.функция тарелки

Например: каждый раз, когда нажимается кнопка для получения данных, мы инициируем действие FETCH_REQUESTED. Мы хотим обработать это действие, запустив задачу для получения некоторых данных с сервера, что-то вроде

window.addEventLister('xxx',fn)

При отправке xxx будет выполнен метод fn,

Сначала мы создаем задачу, которая будет выполнять асинхронное действие (то есть fn выше):

// put:你就认为put就等于 dispatch就可以了;

// call:可以理解为实行一个异步函数,是阻塞型的,只有运行完后面的函数,才会继续往下;
// 在这里可以片面的理解为async中的await!但写法直观多了!
import { call, put } from 'redux-saga/effects'

export function* fetchData(action) {
   try {
      const apiAjax = (params) => fetch(url, params);
      const data = yield call(apiAjax);
      yield put({type: "FETCH_SUCCEEDED", data});
   } catch (error) {
      yield put({type: "FETCH_FAILED", error});
   }
}

Затем запускайте вышеуказанную задачу каждый раз, когда инициируется действие FETCH_REQUESTED, котороеЭто эквивалентно выполнению вышеуказанной задачи каждый раз, когда запускается действие с именем FETCH_REQUESTED., код показывает, как показано ниже

import { takeEvery } from 'redux-saga'

function* watchFetchData() {

  yield* takeEvery("FETCH_REQUESTED", fetchData)
}

Уведомление: Вышеупомянутая функция takeEvery может быть заменена следующей записью

function* watchFetchData() {
  
   while(true){
     yield take('FETCH_REQUESTED');
     yield fork(fetchData);
   }
}
  • takeLatest

В приведенном выше примере takeEveryРазрешить одновременный запуск нескольких экземпляров fetchData, в определенный момент мы можем запустить новую задачу fetchData, даже если одна или несколько предыдущих fetchData не завершились

если мыПросто хочу получить ответ на последний запрос(например, чтобы всегда показывать последнюю версию данных), мы можем использовать вспомогательную функцию takeLatest

import { takeLatest } from 'redux-saga'

function* watchFetchData() {
  yield* takeLatest('FETCH_REQUESTED', fetchData)
}

В отличие от takeEvery, takeLatest позволяет выполнять только одну задачу fetchData в любой момент времени, и эта задача запускается последней.Если перед этим уже выполнялась задача, предыдущая задача будет автоматически отменена.

2. Создатели эффектов

Фреймворк redux-saga предоставляет множество функций для создания эффектов, кратко представим наиболее часто используемые в разработке.

  • take(pattern)
  • put(action)
  • call(fn, ...args)
  • fork(fn, ...args)
  • select(selector, ...args)

take(pattern)

Функцию взятия можно понимать как отслеживание будущих действий.Она создает объект команды и указывает промежуточному ПО ожидать определенного действия.Генератор приостанавливается до тех пор, пока не будет инициировано действие, соответствующее шаблону, и будет продолжать выполняться следующий оператор, который то есть взять блокирующий эффект

Применение:

function* watchFetchData() {
   while(true) {
   // 监听一个type为 'FETCH_REQUESTED' 的action的执行,直到等到这个Action被触发,才会接着执行下面的 		yield fork(fetchData)  语句
     yield take('FETCH_REQUESTED');
     yield fork(fetchData);
   }
}

put(action)

Функция put — это эффект, используемый для отправки действия, вы можете простоПонимайте это как функцию отправки в фреймворке редукции., когда действие поставлено, редьюсер рассчитает новое состояние и вернется,Уведомление: put также является блокирующим эффектом

Применение:

export function* toggleItemFlow() {
    let list = []
    // 发送一个type为 'UPDATE_DATA' 的Action,用来更新数据,参数为 `data:list`
    yield put({
      type: actionTypes.UPDATE_DATA,
      data: list
    })
}

call(fn, ...args)

Вы можете просто понимать функцию вызова как функцию, которая может вызывать другие функции., он приказывает промежуточному ПО вызывать функцию fn, args — параметр функции,Уведомление: Функция fn может быть функцией-генератором или обычной функцией, которая возвращает обещание., функция вызова такжеблокирующий эффект

Применение:

export const delay = ms => new Promise(resolve => setTimeout(resolve, ms))

export function* removeItem() {
  try {
    // 这里call 函数就调用了 delay 函数,delay 函数为一个返回promise 的函数
    return yield call(delay, 500)
  } catch (err) {
    yield put({type: actionTypes.ERROR})
  }
}

fork(fn, ...args)

Функция fork очень похожа на функцию call,используются для вызова других функций, но функция fork является неблокирующей функцией, то есть,программа завершенаyield fork(fn, args)После этой строки кода следующая строка кода будет выполнена немедленно, не дожидаясь, пока функция fn вернет результат., после выполнения следующего оператора

Применение:

import { fork } from 'redux-saga/effects'

export default function* rootSaga() {
  // 下面的四个 Generator 函数会一次执行,不会阻塞执行
  yield fork(addItemFlow)
  yield fork(removeItemFlow)
  yield fork(toggleItemFlow)
  yield fork(modifyItem)
}

select(selector, ...args)

Функция select используется для указания промежуточному программному обеспечению вызывать предоставленный селектор для получения данных о состоянии в Store.Вы также можете просто понимать это какВ фреймворке redux та же функция получения данных о состоянии в хранилище:store.getState()

Применение:

export function* toggleItemFlow() {
     // 通过 select effect 来获取 全局 state上的 `getTodoList` 中的 list
     let tempList = yield select(state => state.getTodoList.list)
}

конкретный пример

**index.js **

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, applyMiddleware} from 'redux'
import createSagaMiddleware from 'redux-saga'

import rootSaga from './sagas'
import Counter from './Counter'
import rootReducer from './reducers'

const sagaMiddleware = createSagaMiddleware() // 创建了一个saga中间件实例

// 下边这句话和下边的两行代码创建store的方式是一样的
// const store = createStore(reducers,applyMiddlecare(middlewares))

const createStoreWithMiddleware = applyMiddleware(middlewares)(createStore)
const store = createStoreWithMiddleware(rootReducer)

sagaMiddleware.run(rootSaga)

const action = type => store.dispatch({ type })

function render() {
  ReactDOM.render(
    <Counter
      value={store.getState()}
      onIncrement={() => action('INCREMENT')}
      onDecrement={() => action('DECREMENT')}
      onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
    document.getElementById('root')
  )
}

render()

store.subscribe(render)

sagas.js

import { put, call, take,fork } from 'redux-saga/effects';
import { takeEvery, takeLatest } from 'redux-saga'

export const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

function* incrementAsync() {
  // 延迟 1s 在执行 + 1操作
  yield call(delay, 1000);
  yield put({ type: 'INCREMENT' });
}

export default function* rootSaga() {
  // while(true){
  //   yield take('INCREMENT_ASYNC');
  //   yield fork(incrementAsync);
  // }

  // 下面的写法与上面的写法上等效
  yield* takeEvery("INCREMENT_ASYNC", incrementAsync)
}

reducer.js

export default function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    case 'INCREMENT_ASYNC':
      return state
    default:
      return state
  }
}

Как видно из приведенной выше структуры кода, использование redux-saga относительно простое, по сравнению с CounterApp предыдущего фреймворка redux, здесь есть дополнительный файл sagas, а файл reducers по-прежнему используется раньше.

Краткое изложение основного использования redux-saga:

  1. Используйте метод createSagaMiddleware для создания промежуточного программного обеспечения саги, а затем используйте функцию applyMiddleware для привязки созданного экземпляра промежуточного программного обеспечения саги к хранилищу при создании хранилища избыточности и, наконец, вызовите функцию запуска промежуточного программного обеспечения саги для выполнения одного или нескольких промежуточного программного обеспечения.
  2. В ПО промежуточного слоя Saga API-интерфейсы, такие как takeEvery или takeLatest, можно использовать для отслеживания действия. Когда действие запускается, saga может использовать call для инициирования асинхронной операции. После завершения операции используйте функцию put для запуска действия и обновления. состояние синхронно, тем самым завершая весь процесс Обновление состояния.

Хорошо, на этом история подходит к концу. Вышеизложенное в основном представляет некоторые основные продукты Redux, React-Redux и Redux-Saga в настоящее время. Далее мы будем в основном производить в соответствии с исходным кодом,Напишите колесо редукции и реагируйте-редукс вручную

Надеюсь, что вам понравится, обратите внимание и поощрите вас. Если у вас есть какие-то недостатки, пожалуйста, исправьте их ~