Redux:从action到saga

简介:

前端应用消失的部分

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

  1. 一个存储了应用不可变状态(state)的store
  2. 状态(state)可以被绘制在组件里(html或者其他的东西)。这个绘制方法通常是简单而且可测试的(并不总是如此)纯方法。

    const render = (state) => components
  3. 组件可以给store分发action
  4. 使用reducer这种纯方法来根据就的状态返回新的状态

    const reducer = (oldState, action) => newState
  5. 从一再来一次

这看起来容易理解。但是当需要处理异步的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
  3. 组件会触发修改store的action。
  4. state使用reducer这样的纯方法来修改的。

    const reducer = (oldState, action) => newState
  5. 也许某些effect会被一个action或者其他的effect触发。

    function* saga() { yield effect; }
  6. 从1开始。

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

欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!

















本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/7865516.html ,如需转载请自行联系原作者



相关文章
|
Java Windows
JDK 1.8(Windows版)安装教程
JDK 1.8(Windows版)安装教程
465 1
【Copula】考虑风光联合出力和相关性的Copula场景生成(Matlab代码实现)
【Copula】考虑风光联合出力和相关性的Copula场景生成(Matlab代码实现)
244 1
|
消息中间件 存储 数据采集
从Kafka到Pulsar:数据流演进之路
从Kafka到Pulsar:数据流演进之路
316 0
从Kafka到Pulsar:数据流演进之路
|
7天前
|
存储 关系型数据库 分布式数据库
PostgreSQL 18 发布,快来 PolarDB 尝鲜!
PostgreSQL 18 发布,PolarDB for PostgreSQL 全面兼容。新版本支持异步I/O、UUIDv7、虚拟生成列、逻辑复制增强及OAuth认证,显著提升性能与安全。PolarDB-PG 18 支持存算分离架构,融合海量弹性存储与极致计算性能,搭配丰富插件生态,为企业提供高效、稳定、灵活的云数据库解决方案,助力企业数字化转型如虎添翼!
|
6天前
|
存储 人工智能 Java
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
本文讲解 Prompt 基本概念与 10 个优化技巧,结合学术分析 AI 应用的需求分析、设计方案,介绍 Spring AI 中 ChatClient 及 Advisors 的使用。
314 130
AI 超级智能体全栈项目阶段二:Prompt 优化技巧与学术分析 AI 应用开发实现上下文联系多轮对话
|
18天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1329 8
|
5天前
|
监控 JavaScript Java
基于大模型技术的反欺诈知识问答系统
随着互联网与金融科技发展,网络欺诈频发,构建高效反欺诈平台成为迫切需求。本文基于Java、Vue.js、Spring Boot与MySQL技术,设计实现集欺诈识别、宣传教育、用户互动于一体的反欺诈系统,提升公众防范意识,助力企业合规与用户权益保护。
|
17天前
|
机器学习/深度学习 人工智能 前端开发
通义DeepResearch全面开源!同步分享可落地的高阶Agent构建方法论
通义研究团队开源发布通义 DeepResearch —— 首个在性能上可与 OpenAI DeepResearch 相媲美、并在多项权威基准测试中取得领先表现的全开源 Web Agent。
1407 87