一篇文章介绍redux、react-redux、redux-saga总结

本篇主要将react全家桶的产品非常精炼的提取了核心内容,精华程度堪比精油。各位大人,既然来了,客官您坐,来人,给客官看茶~~

redux

前言

首先,本篇文章要求您对js,react等知识有一定的了解,如果不曾了解,建议您先看一下:React精髓!一篇全概括(急速)

React有props和state:

  1. props意味着父级分发下来的属性
  2. state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,这就是react的单向数据流

这就意味着如果是一个数据状态非常复杂的应用,更多的时候发现React根本无法让两个组件互相交流,使用对方的数据,react的通过层级传递数据的这种方法是非常难受的,这个时候,迫切需要一个机制,把所有的state集中到组件顶部,能够灵活的将所有state各取所需的分发给所有的组件,是的,这就是redux

简介

  1. redux是的诞生是为了给 React 应用提供「可预测化的状态管理」机制。
  2. Redux会将整个应用状态(其实也就是数据)存储到到一个地方,称为store
  3. 这个store里面保存一棵状态树(state tree)
  4. 组件改变state的唯一方法是通过调用store的dispatch方法,触发一个action,这个action被对应的reducer处理,于是state完成更新
  5. 组件可以派发(dispatch)行为(action)给store,而不是直接通知其它组件
  6. 其它组件可以通过订阅store中的状态(state)来刷新自己的视图

使用步骤

创建reducer

  • 可以使用单独的一个reducer,也可以将多个reducer合并为一个reducer,即:combineReducers()
  • action发出命令后将state放入reucer加工函数中,返回新的state,对state进行加工处理

创建action

  • 用户是接触不到state的,只能有view触发,所以,这个action可以理解为指令,需要发出多少动作就有多少指令
  • action是一个对象,必须有一个叫type的参数,定义action类型

创建的store,使用createStore方法

  • store 可以理解为有多个加工机器的总工厂
  • 提供subscribe,dispatch,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}

自己画了一张非常简陋的流程图,方便理解redux的工作流程

react-redux

刚开始就说了,如果把store直接集成到React应用的顶层props里面,只要各个子组件能访问到顶层props就行了,比如这样:

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

不就ok了吗?这就是 react-redux。Redux 官方提供的 React 绑定库。 具有高效且灵活的特性。

React Redux 将组件区分为 容器组件 和 UI 组件

  1. 前者会处理逻辑
  2. 后者只负责显示和交互,内部不处理逻辑,状态完全由外部掌控

两个核心

Provider

看我上边那个代码的顶层组件4个字。对,你没有猜错。这个顶级组件就是Provider,一般我们都将顶层组件包裹在Provider组件之中,这样的话,所有组件就都可以在react-redux的控制之下了,但是store必须作为参数放到Provider组件中去

<Provider store = {store}>
 <App />
<Provider>

这个组件的目的是让所有组件都能够访问到Redux中的数据。

connect

这个才是react-redux中比较难的部分,我们详细解释一下

首先,先记住下边的这行代码:

connect(mapStateToProps, mapDispatchToProps)(MyComponent)

mapStateToProps

这个单词翻译过来就是把state映射到props中去 ,其实也就是把Redux中的数据映射到React中的props中去。
举个栗子:

 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

这个单词翻译过来就是就是把各种dispatch也变成了props让你可以直接使用

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,来调用dispatch,这样子就不需要在代码中来进行store.dispatch了

react-redux的基本介绍就到这里了

redux-saga

如果按照原始的redux工作流程,当组件中产生一个action后会直接触发reducer修改state,reducer又是一个纯函数,也就是不能再reducer中进行异步操作;

而往往实际中,组件中发生的action后,在进入reducer之前需要完成一个异步任务,比如发送ajax请求后拿到数据后,再进入reducer,显然原生的redux是不支持这种操作的

这个时候急需一个中间件来处理这种业务场景,目前最优雅的处理方式自然就是redux-saga

核心讲解

1、Saga 辅助函数

redux-saga提供了一些辅助函数,用来在一些特定的action 被发起到Store时派生任务,下面我先来讲解两个辅助函数:takeEvery 和 takeLatest

takeEvery

takeEvery就像一个流水线的洗碗工,过来一个脏盘子就直接执行后面的洗碗函数,一旦你请了这个洗碗工他会一直执行这个工作,绝对不会停止接盘子的监听过程和触发洗盘子函数

例如:每次点击  按钮去Fetch获取数据时时,我们发起一个 FETCH_REQUESTED 的 action。 我们想通过启动一个任务从服务器获取一些数据,来处理这个action,类似于

window.addEventLister('xxx',fn)

当dispatch xxx的时候,就会执行fn方法,

首先我们创建一个将执行异步 action 的任务(也就是上边的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 action 被发起时启动上面的任务,也就相当于每次触发一个名字为 FETCH_REQUESTED 的action就会执行上边的任务,代码如下

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、Effect Creators

redux-saga框架提供了很多创建effect的函数,下面我们就来简单的介绍下开发中最常用的几种

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

take(pattern)

take函数可以理解为监听未来的action,它创建了一个命令对象,告诉middleware等待一个特定的action, Generator会暂停,直到一个与pattern匹配的action被发起,才会继续执行下面的语句,也就是说,take是一个阻塞的 effect

用法:

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

put(action)

put函数是用来发送action的 effect,你可以简单的把它理解成为redux框架中的dispatch函数,当put一个action后,reducer中就会计算新的state并返回,注意: put 也是阻塞 effect

用法:

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

call(fn, ...args)

call函数你可以把它简单的理解为就是可以调用其他函数的函数,它命令 middleware 来调用fn 函数, args为函数的参数,注意:  fn 函数可以是一个 Generator 函数,也可以是一个返回 Promise 的普通函数,call 函数也是阻塞 effect

用法:

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 函数是用来指示 middleware调用提供的选择器获取Store上的state数据,你也可以简单的把它理解为redux框架中获取store上的 state数据一样的功能 :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的使用方式还是比较简单的,相比较之前的redux框架的CounterApp,多了一个sagas的文件,reducers文件还是之前的使用方式

redux-saga基本用法总结:

  • 使用 createSagaMiddleware 方法创建 saga 的 Middleware ,然后在创建的 redux 的 store 时,使用 applyMiddleware 函数将创建的 saga Middleware 实例绑定到 store 上,最后可以调用 saga Middleware 的 run 函数来执行某个或者某些 Middleware 。
  • 在 saga 的 Middleware 中,可以使用 takeEvery 或者 takeLatest 等 API 来监听某个 action ,当某个 action 触发后, saga 可以使用 call 发起异步操作,操作完成后使用 put 函数触发 action ,同步更新 state ,从而完成整个 State 的更新。

ok,故事到这里就接近尾声了,以上主要介绍了redux,react-redux和redux-saga目前redux全家桶主流的一些产品,接下来,主要会产出一下根据源码,手写一下redux和react-redux的轮子

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 浅谈redux以及react-redux简单实现

    写在前头 redux 简介 随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态). 这些 state 可能包括服务器响应.缓存数据.本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等. 管理不断变化的 state 非常困难.如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,

  • redux-saga 初识和使用

    redux-saga 是一个管理 Redux 应用异步操作的中间件,功能类似redux-thunk + async/await, 它通过创建 Sagas 将所有的异步操作逻辑存放在一个地方进行集中处理. redux-saga 的 effects redux-saga中的 Effects 是一个纯文本 JavaScript 对象,包含一些将被 saga middleware 执行的指令.这些指令所执行的操作包括如下三种: 发起一个异步调用(如发一起一个 Ajax 请求) 发起其他的 action

  • 记录一篇关于redux-saga的基本使用过程

    安装 npm install --save redux npm install --save redux-saga 配置action actionType 创建文件src/actions/types.js,在types.js文件中添加需要的action类型 export const TEST1_ACTION = 'test1'; export const SET_TEST2_ACTION = 'change_test2'; export const SET_TEST3_ACTION = 'cha

  • 关于redux-saga中take使用方法详解

    本文介绍了关于redux-saga中take使用方法详解,分享给大家,具体如下: 带来一个自己研究好久的API使用方法. redux-saga中effect中take这个API使用方式,用的多的是call,put,select,但take这个平常还真没什么机会用上,也不清楚在哪里使用才好,不管怎么样,既然是redux-saga写出来的,肯定是有他的用法的,不管37 21,先学会使用方法再说. 先看看介绍: take take的表现同takeEvery一样,都是监听某个action,但与takeE

  • 原生实现一个react-redux的代码示例

    自己动手实现一个react-redux 之前试过自己动手实现一个redux,这篇blog主要记录动手实现一个react-redux的过程. 这个react-redux还有一点点小瑕疵,我以一个计数器作为例子来实现的. 这是目录结构: 这里的connect.js文件就是react-redux. ├─component │ connect.js │ counter.js │ └─store index.js index.js: import React from "react"; impo

  • 浅谈React和Redux的连接react-redux

    之前一直在探索React相关的东西,手上有个SPA项目,于是准备上Redux试试水.Redux本身和React并没有之间的关联,它是一个通用Javscript App模块,用做App State的管理.要在React的项目中使用Redux,比较好的方式是借助react-redux这个库来做连接,这里的意思是,并不是没有react-redux,这两个库就不弄一起用了,而是说react-redux提供了一些封装,一种更科学的代码组织方式,让我们更舒服地在React的代码中使用Redux. 之前仅通过

  • 详解react、redux、react-redux之间的关系

    本文介绍了react.redux.react-redux之间的关系,分享给大家,也给自己留个笔记,具体如下: React 一些小型项目,只使用 React 完全够用了,数据管理使用props.state即可,那什么时候需要引入Redux呢? 当渲染一个组件的数据是通过props从父组件中获取时,通常情况下是 A --> B,但随着业务复杂度的增加,有可能是这样的:A --> B --> C --> D --> E,E需要的数据需要从A那里通过props传递过来,以及对应的 E

  • 一篇文章介绍redux、react-redux、redux-saga总结

    本篇主要将react全家桶的产品非常精炼的提取了核心内容,精华程度堪比精油.各位大人,既然来了,客官您坐,来人,给客官看茶~~ redux 前言 首先,本篇文章要求您对js,react等知识有一定的了解,如果不曾了解,建议您先看一下:React精髓!一篇全概括(急速) React有props和state: props意味着父级分发下来的属性 state意味着组件内部可以自行管理的状态,并且整个React没有数据向上回溯的能力,这就是react的单向数据流 这就意味着如果是一个数据状态非常复杂的应

  • React与Redux之数组处理讲解

    本文将介绍一些常用的数组处理函数和语法,如reduce().filter().map().every().some().展开运算符.这些知识和React与Redux本身没有直接关系,但是本章的示例中包含了这些函数和语法的用法,我们正好可以在程序中学习它们.另外值得一提的是,reduce().filter().map()等函数的思想来自函数式编程,感谢JavaScript是一门函数式编程语言,让原本复杂的逻辑处理变得如此简单. reduce() 概述 reduce()方法接收一个函数作为累加器(a

  • 深入学习TypeScript 、React、 Redux和Ant-Design的最佳实践

    前言 阿特伍德定律,指的是any application that can be written in JavaScript, will eventually be written in JavaScript,意即"任何可以用JavaScript来写的应用,最终都将用JavaScript来写" 在使用新技术的时候,切忌要一步一步的来,如果当你尝试把两门不熟悉的新技术一起结合使用,你很大概率会被按在地上摩擦,会yarn/npm和React脚手架等技术是前提,后面我会继续写PWA深入和No

  • react用Redux中央仓库实现一个todolist

    本文实例为大家分享了react用Redux中央仓库实现一个todolist的具体代码,供大家参考,具体内容如下 Redux简单介绍 Redux是一个用来管理管理数据状态和UI状态的JavaScript应用工具.随着JavaScript单页应用(SPA)开发日趋复杂,JavaScript需要管理比任何时候都要多的state(状态),Redux就是降低管理难度的.(Redux支持React,Angular.jQuery甚至纯JavaScript) Redux Dev Tools插件 Redux调试工

  • React中Redux核心原理深入分析

    目录 一.Redux是什么 二.Redux的核心思想 三.Redux中间件原理 四.手写一个Redux 总结 一.Redux是什么 众所周知,Redux最早运用于React框架中,是一个全局状态管理器.Redux解决了在开发过程中数据无限层层传递而引发的一系列问题,因此我们有必要来了解一下Redux到底是如何实现的? 二.Redux的核心思想 Redux主要分为几个部分:dispatch.reducer.state. 我们着重看下dispatch,该方法是Redux流程的第一步,在用户界面中通过

  • 如何使用Redux Toolkit简化Redux

    了解Redux Toolkit,这是用于高效Redux开发的经过验证的工具集.在本文中,你将看到为什么Redux Toolkit值得React社区更多的关注. React和Redux被认为是大规模React应用中管理状态的最佳组合.然而,随着时间的推移,Redux的受欢迎程度下降,原因是: 配置Redux Store并不简单. 我们需要几个软件包来使Redux与React一起工作. Redux需要太多样板代码. 带着这些问题,Redux的创建者Dan Abramov发表了名为<你可能不需要Red

  • 一篇文章入门Python生态系统(Python新手入门指导)

    译者按:原文写于2011年末,虽然文中关于Python 3的一些说法可以说已经不成立了,但是作为一篇面向从其他语言转型到Python的程序员来说,本文对Python的生态系统还是做了较为全面的介绍.文中提到了一些第三方库,但是Python社区中强大的第三方库并不止这些,欢迎各位Pytonistas补充. •原文链接:http://mirnazim.org/writings/python-ecosystem-introduction/ •译文链接:http://codingpy.com/artic

  • 一篇文章就能了解Rxjava

    前言: 第一次接触RxJava是在前不久,一个新Android项目的启动,在评估时选择了RxJava.RxJava是一个基于事件订阅的异步执行的一个类库.听起来有点复杂,其实是要你使用过一次,就会大概明白它是怎么回事了!为是什么一个Android项目启动会联系到RxJava呢?因为在RxJava使用起来得到广泛的认可,又是基于Java语言的.自然会有善于组织和总结的开发者联想到Android!没错,RxAndroid就这样在RxJava的基础上,针对Android开发的一个库.今天我们主要是来讲

  • 一篇文章搞定JavaScript类型转换(面试常见)

    为啥要说这个东西?一道面试题就给我去说它的动机. 题如下: var bool = new Boolean(false); if (bool) { alert('true'); } else { alert('false'); } 运行结果是true!!! 其实啥类型转换啊,操作符优先级啊,这些东西都是最最基本的.犀牛书上有详细的介绍.但我很少去翻犀牛书的前5章... 比如说优先级那块儿,很多书都教育我们,"不用去背诵优先级顺序,不确定的话,加括号就行了."平常我们写代码时也确实这么做的

随机推荐