用于生成器协程的 Redux 中间件。

安装

如果你尚未安装 Node,请先安装。

使用 npm 将该模块安装到你的项目中:

npm install @wordpress/redux-routine

@wordpress/redux-routine 同时使用了 Promise 和 Generator,这是 JavaScript 语言中的两项现代特性。如果你需要支持较旧的浏览器(Internet Explorer 11 或更早版本),则需要自行提供 polyfill。

用法

@wordpress/redux-routine 的默认导出是一个函数,该函数接收一个控制处理函数对象,并返回一个 Redux 中间件函数。

例如,考虑一个常见场景:我们需要发起一个网络请求。我们可以在创建中间件时将网络请求定义为控制处理函数。

import { combineReducers, createStore, applyMiddleware } from 'redux';
import createMiddleware from '@wordpress/redux-routine';

const middleware = createMiddleware( {
    async FETCH_JSON( action ) {
        const response = await window.fetch( action.url );
        return response.json();
    },
} );

function temperature( state = null, action ) {
    switch ( action.type ) {
        case 'SET_TEMPERATURE':
            return action.temperature;
    }

    return state;
}

const reducer = combineReducers( { temperature } );

const store = createStore( reducer, applyMiddleware( middleware ) );

function* retrieveTemperature() {
    const result = yield { type: 'FETCH_JSON', url: 'https://' };
    return { type: 'SET_TEMPERATURE', temperature: result.value };
}

store.dispatch( retrieveTemperature() );

在这个示例中,当我们 dispatch retrieveTemperature 时,会触发控制处理函数生效,发起网络请求,并将结果赋值给 result 变量。只有在请求完成后,
动作创建器才会继续返回 SET_TEMPERATURE 动作类型。

API

default

给定一个控制项对象以创建 Redux 中间件;其中每个键是要处理的动作类型,值是一个函数,该函数返回一个 Promise(在动作求值应继续时 resolve)或一个值。该值或 Promise resolve 后的值会被赋给 yield 赋值的返回值。如果控制处理函数返回 undefined,则不会继续执行。

参数

  • controls Record< string, ( value: AnyAction ) => Promise< boolean > | boolean >:控制处理函数对象。

返回值

  • Middleware:协程运行时

动机

@wordpress/redux-routine 与其他类似的基于生成器的 Redux 副作用解决方案(包括 redux-saga)有许多相同的动机。它的不同之处在于,凭借极简主义而更少既定约定。它不包含默认控制项,不提供用于拆分逻辑流的工具,也不自带任何错误处理。这样做是为了提升易用性,让希望为应用引入异步或条件延续流程的开发者无需经历陡峭的学习曲线。

主要动机包括但不限于:

  • 可测试性:由于动作创建器 yield 的是普通动作对象,因此在测试中可以轻松替换其解析行为。
  • 受控的灵活性:可以在不牺牲动作类型的表达力和意图性的情况下实现控制流。像 thunk 或 Promise 等其他方案提供了极致灵活性,但代价是可维护性下降,表现为动作类型与偶然实现之间的深度耦合。
  • 用于表达数据流的 通用领域语言:由于控制项是集中定义的,因此开发团队需要有意识地决定何时以及如何添加新的控制处理函数。

测试

由于你的动作创建器会返回普通动作对象的可迭代生成器,因此测试非常简单。

再次考虑上面的示例:

function* retrieveTemperature() {
    const result = yield { type: 'FETCH_JSON', url: 'https://' };
    return { type: 'SET_TEMPERATURE', temperature: result.value };
}

一个测试用例(使用 Node 的内置 assert 模块)可以这样写:

import { deepEqual } from 'assert';

const action = retrieveTemperature();

deepEqual( action.next().value, {
    type: 'FETCH_JSON',
    url: 'https://',
} );

const jsonResult = { value: 10 };
deepEqual( action.next( jsonResult ).value, {
    type: 'SET_TEMPERATURE',
    temperature: 10,
} );

如果你的动作创建器没有将 yield 的结果赋值给变量,你也可以使用 Array.from 从动作创建器的结果创建数组。

为此包做贡献

这是 Gutenberg 项目中的一个独立包。该项目被组织为一个单一仓库(monorepo)。它由多个自包含的软件包组成,每个包都有特定用途。此单一仓库中的包发布到 npm,并被 WordPress 及其他软件项目使用。

如需了解更多关于为此包或整个 Gutenberg 做贡献的信息,请阅读项目的主要 贡献者指南。