前端应用消失的部分

一个现代的、使用了redux的前端应用架构可以这样描述:

  1. 一个存储了应用不可变状态(state)的store
  2. 状态(state)可以被绘制在组件里(html或者其他的东西)。这个绘制方法通常是简单而且可测试的(并不总是如此)纯方法。
const render = (state) => components
  1. 组件可以给store分发action
  2. 使用reducer这种纯方法来根据就的状态返回新的状态
const reducer = (oldState, action) => newState
  1. 从一再来一次

这看起来容易理解。但是当需要处理异步的action(在函数式编程里称为副作用)的时候事情就没有这么简单了。

为了解决这个问题,redux建议使用中间件(尤其是thunk)。基本上,如果你需要出发副作用(side effects),使用一种特定的action生成方法:一种返回一个方法的方法,可以实现任意的异步访问并分发任意你想要的action。

使用这个方式会很快导致action生成方法变得复杂并难以测试。这个时候就需要redux-saga了。在redux-saga里saga就是一个可声明的组织良好的副作用实现方式(超时,API调用等等。。)所以不用再用redux-thunk中间件来写,我们用saga来发出action并yield副作用。

这样不复杂?action creator这样的写法不是更简单?

虽然看起来是这样的,但是NO!

我们来看看如何写一个action creator来获取后端数据并分发到redux store。

function loadTodos() {
return dispatch => {
dispatch({ type: 'FETCH_TOTOS' });
fetch('/todos').then(todos => {
dispatch({ type: 'FETCH_TODOS', payload: todos });
});
}
}

这是最简单的thunk action creator了,并且如你所见,唯一测试这个代码的方法是模拟获取数据的方法。

我们来看看用saga代替action creator获取todo数据的方法:

import { call, put } from 'redux-saga';

function* loadTodos() {
yield put({ type: 'FETCH_TODOS' });
const todos = yield call(fetch, '/todos');
yield put({ type: 'FETCH_TODOS', payload: todos });
}

正如你所见一个saga就是一个生成副作用(side effects)的generator。我(作者)更加倾向于把整个generator叫做纯generator,因为它不会实际执行副作用,只会生成一个要执行的副作用的描述。在上面的例子中我用了两种副作用:

  • 一个put副作用,它会给redux store分发一个action。
  • 一个call副作用,它会执行一个异步的方法(promise,cps后者其他的saga)。

现在,测试这个saga就非常的容易了:

import { call, put } from 'redux-saga';

const mySaga = loadTodos();
const myTodos = [{ message: 'text', done: false }];
mySaga.next();
expect(mySaga.next().value).toEqual(put({ type: 'FETCH_TOTOS' }));
expect(mySaga.next().value).toEqual(call(fetch, '/todos'));
expect(mySaga.next().value).toEqual(put({ type: 'FETCH_TODOS', payload: myTodos }));

触发一个saga

thunk的action creator在分发它返回的方法的时候就会触发。saga不同,它们就像是运行在后台的守护任务(daemon task)一样有自己的运行逻辑(by Yasine Elouafi redux-saga的作者)。

所以,我们来看看如何在redux应用里添加saga。

import { createStore, applyMiddleware } from 'redux';
import sagaMiddleware from 'redux-saga'; const createStoreWithSaga = applyMiddleware(
sagaMiddleware([loadTodos])
)(createStore); let store = createStoreWithSaga(reducer, initialState);

绑定saga

一个saga本身就是一个副作用,就如同redux的reducer一样,绑定saga非常简单(但是很好的理解ES6的generator是非常有必要的)。

在之前的例子里,loadTodos saga在一开始就会触发。但是,如果我们想要每次一个action分发到store的时候触发saga要怎么做呢?看代码:

import { fork, take } from 'redux-saga';

function* loadTodos() {
yield put({ type: 'FETCHING_TODOS' });
const todos = yield call(fetch, '/todos');
yield put({ type: 'FETCHED_TODOS', payload: todos });
} function* watchTodos() {
while(yield take('FETCH_TODOS')) {
yield fork(loadTodos);
}
} // 我们需要更新saga常量 createStoreWithSaga = applyMiddleware(
sagaMiddleware([watchTodos])
)(createStore);

上例用到了两个特殊的effect:

  • take effect,它会等待分发redux action的时候执行
  • fork effect, 它会触发另外一个effect,在子effect开始之前就会执行

结语

给前端应用添加redux和redux-saga的流程是这样的:

  1. store持有一个应用的state。
  2. state会被绘制到组件上(html或者其他的什么)。它是一个简单可测试的方法:
const render = (state) => components
  1. 组件会触发修改store的action。
  2. state使用reducer这样的纯方法来修改的。
const reducer = (oldState, action) => newState
  1. 也许某些effect会被一个action或者其他的effect触发。
function* saga() { yield effect; }
  1. 从1开始。

原文链接:https://riad.blog/2015/12/28/redux-nowadays-from-actions-creators-to-sagas/

Redux:从action到saga的更多相关文章

  1. [Redux] Avoid action type naming conflicts

    In redux, the action type is just a normal string type, it is easy to get naming conflicts in large ...

  2. [Redux] Extracting Action Creators

    We will create an anction creator to manage the dispatch actions, to keep code maintainable and self ...

  3. Redux系列01:从一个简单例子了解action、store、reducer

    其实,redux的核心概念就是store.action.reducer,从调用关系来看如下所示 store.dispatch(action) --> reducer(state, action) ...

  4. [Full-stack] 状态管理技巧 - Redux

    资源一: In React JS Tutorials, lectures from 9. From: React高级篇(一)从Flux到Redux,react-redux 从Flux到Redux,再到 ...

  5. 3.4 redux 异步

    在大多数的前端业务场景中,需要和后端产生异步交互,在本节中,将详细讲解 redux 中的异步方案以及一些异步第三方组件,内容有: redux 异步流 redux-thunk redux-promise ...

  6. 实例讲解react+react-router+redux

    前言 总括: 本文采用react+redux+react-router+less+es6+webpack,以实现一个简易备忘录(todolist)为例尽可能全面的讲述使用react全家桶实现一个完整应 ...

  7. redux介绍与入门

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 20.0px Helvetica } p.p2 { margin: 0.0px 0.0px 0.0px 0. ...

  8. Redux教程1:环境搭建,初写Redux

    如果将React比喻成士兵的话,你的程序还需要一位将军,去管理士兵(的状态),而Redux恰好是一位好将军,简单高效: 相比起React的学习曲线,Redux的稍微平坦一些:本系列教程,将以" ...

  9. 【原】redux学习笔记

    上周学习了flux,这周研究了一下redux,其实很早之前都已经在研究他们了,只是之前一直没搞懂,最近这两周可能打通了任督二脉,都算入门了. 写博客的目的主要是做一下笔记,总结一下思路,以及和大家交流 ...

随机推荐

  1. 流API--初体验

    在JDK8新增的许多功能中,有2个功能最重要,一个是Lambda表达式,一个是流API.Lambda表达式前面我已经整理过了,现在开始整理流API.首先应该如何定义流API中的"流" ...

  2. 为mysql数据库建立索引

    前些时候,一位颇高级的程序员居然问我什么叫做索引,令我感到十分的惊奇,我想这绝不会是沧海一粟,因为有成千上万的开发者(可能大部分是使用MySQL的)都没有受过有关数据库的正规培训,尽管他们都为客户做过 ...

  3. iOS 组件化 —— 路由设计思路分析

    原文 前言 随着用户的需求越来越多,对App的用户体验也变的要求越来越高.为了更好的应对各种需求,开发人员从软件工程的角度,将App架构由原来简单的MVC变成MVVM,VIPER等复杂架构.更换适合业 ...

  4. H3c交换机配置端口镜像详情

    端口镜像 需要将G0/0/1口的全部流量镜像到G0/0/2口,即G0/0/1为源端口,G0/0/2为目的端口. 配置步骤 1.进入配置模式:system-view: 2.创建本地镜像组:mirrori ...

  5. 通过重写 class 的 ToString() 来简化获取 enum 的 DescriptionAttribute 值

    通过重写 class 的 ToString() 来简化获取 enum 的 DescriptionAttribute 值 目录 一.常见的 enum 类型 二.演变:class 版本的 enum 类型 ...

  6. [DeeplearningAI笔记]ML strategy_2_2训练和开发/测试数据集不匹配问题

    机器学习策略-不匹配的训练和开发/测试数据 觉得有用的话,欢迎一起讨论相互学习~Follow Me 2.4在不同分布上训练和测试数据 在深度学习时代,越来越多的团队使用和开发集/测试集不同分布的数据来 ...

  7. 用swing做一个简单的正则验证工具

    直接上代码吧,因为我对swing也不熟悉,照着API一点点拼出来的. import java.awt.event.ActionEvent; import java.awt.event.ActionLi ...

  8. Trusted Execution Technology (TXT) --- 启动控制策略(LCP)篇

    版权声明:本文为博主原创文章,未经博主允许不得转载.http://www.cnblogs.com/tsec/p/8428631.html 在TXT平台中,启动控制策略(Launch Control P ...

  9. c#结构体、打他table、excel、csv互转

    1.csv相关 public static class CsvHelper { /// <summary> /// 根据csv路径获取datatable /// </summary& ...

  10. python监控接口请求

    #!/usr/bin/env python #coding=utf8 import time,os,sched,urllib,httplib import smtplib import string ...