canonical-reducer-composition:规范化的减速器组成设计模式规范

  • p5_190635
    了解作者
  • 2.7KB
    文件大小
  • zip
    文件格式
  • 0
    收藏次数
  • VIP专享
    资源类型
  • 0
    下载次数
  • 2022-06-14 02:47
    上传日期
规范化的减速器组成 规格 规范化的减速器组成模式要求: 减速器定义 Reducer定义必须注册至少一个域。 操作名称必须与操作name属性值相对应。 操作名称在整个化简器定义对象中必须唯一。 领域 域名必须拥有唯一的子域或动作处理程序。 域可以拥有另一个域。 域可以拥有动作处理程序。 域可以拥有CONSTRUCT动作处理程序。 动作处理程序 动作处理程序必须是一个函数。 操作处理程序必须发生变异它的参数。 动作处理程序必须返回域状态。 行动 动作必须是普通对象。 动作必须定义name属性。 动作name属性值必须是字符串。 动作name属性值必须仅包含大写拉丁字符和一个或多个下划线字符( /^[AZ\_]+$/ )。 动作可以定义data属性。 定义后, data属性值必须是普通对象。 动作可以定义metadata属性。 定义后, metadata属性值必须是普
canonical-reducer-composition-master.zip
  • canonical-reducer-composition-master
  • README.md
    7.8KB
内容介绍
# Canonical Reducer Composition * [Spec](#spec) * [Reducer Definition](#reducer-definition) * [Domain](#domain) * [Action](#action) * [Flux Standard Action](#flux-standard-action) * [`CONSTRUCT` Action Handler](#construct-action-handler) * [Schema](#schema) * [Implementation Example](#implementation-example) * [Benefits](#benefits) * [Redux Reducer Composition](#redux-reducer-composition) * [Validator Library](#validator-library) * [Libraries](#libraries) ## Spec Canonical Reducer Composition pattern requires that: ### Reducer Definition * Reducer definition **must** register at least one domain. * Action name **must** correspond to the action `name` property value. * Action name **must** be unique in the entire reducer definition object. ### Domain * Domain **must** own only sub-domains or action handlers. * Domain **can** own another domain. * Domain **can** own action handlers. * Domain **can** own [`CONSTRUCT` action handler](#construct-action-handler). ### Action Handler * Action handler **must** be a function. * Action handler **must** not mutate its arguments. * Action handler **must** return domain state. ### Action * Action **must** be a plain object. * Action **must** define `name` property. * Action `name` property value **must** be a string. * Action `name` property value **must** consist only of uppercase latin characters and one or more underscore characters (`/^[A-Z\_]+$/`). * Action **can** define `data` property. When defined, * `data` property value **must** be a plain object. * Action **can** define `metadata` property. When defined, * `metadata` property value **must** be a plain object. * Action **can** define `error` property. When defined, * It **must** be an object. * It **can** be an instance of [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error). * It **must** have `message` property. * `message` property value **must** be a string. ## Flux Standard Action [Flux Standard Action](https://github.com/acdlite/flux-standard-action) (FSA) is a competing standard. If you are implementing software using Canonical Reducer Composition and have dependencies that use FSA convention, you can use [`redux-convention`](https://github.com/gajus/redux-convention) middleware to convert between the two standards. ## `CONSTRUCT` Action Handler * A domain can register `CONSTRUCT` action handler. * `CONSTRUCT` can be used to construct the initial domain state. The application must send `{name: 'CONSTRUCT'}` action to initialise the domain state, e.g. ```js import { createStore } from 'redux'; import { combineReducers } from 'redux-immutable'; import * as reducers from './reducers'; import Immutable from 'immutable'; let reducer, state, store; reducer = combineReducers(reducers); state = Immutable.Map({}); // Invoking CONSTRUCT to build the initial state. state = reducer(state, { name: 'CONSTRUCT' }); store = createStore(reducer, state); ``` ## Schema Reducer definition with a single domain: ```js { <domain>: { <action handler rel='nofollow' onclick='return false;'> (domain, action) { } } } ``` In addition, domain can define a sub-domain: ```js { <domain>: { <domain>: { /** * Constructs the initial domain state. * * @param {Object} domain * @return {Object} */ CONSTRUCT (domain) { }, /** * @typedef Action * @see {@link https://github.com/gajus/canonical-reducer-composition#action} * @property {String} name */ /** * @param {Object} domain * @param {Action} action */ <action handler rel='nofollow' onclick='return false;'> (domain, action) { }, <action handler rel='nofollow' onclick='return false;'> (domain, action) { } }, <domain>: { <action handler rel='nofollow' onclick='return false;'> (domain, action) { } } } } ``` ## Benefits Canonical Reducer Composition has the following benefits: * Introduces reducer declaration convention. * Domain reducer function is called only if it registers an action. * Enables logging of unhandled actions. * Enables intuitive nesting of the domain model. ## Implementation Example ```js import { createStore, } from 'redux'; import { combineReducers } from 'redux-immutable'; import Immutable from 'immutable'; let reducer, state, store; state = { // <domain> countries: [ 'IT', 'JP', 'DE' ], // <domain> cities: [], // <domain> user: { // <domain> names: [ 'Gajus', 'Kuizinas' ] } } reducer = { countries: { ADD_COUNTRY: (domain, action) { return domain.push(action.country); }, REMOVE_COUNTRY: (domain, action) { return domain.delete(domain.indexOf(action.country)); } }, cities: { // Using a constructor. CONSTRUCT () { return [ 'Rome', 'Tokyo', 'Berlin' ]; }, ADD_CITY (domain, action) { return domain.push(action.city); }, REMOVE_CITY (domain, action) { return domain.delete(domain.indexOf(action.city)); } }, // Implement a sub-domain reducer map. user: { names: { ADD_NAME (domain, action) { return domain.push(action.name); }, REMOVE_NAME (domain, action) { return domain.delete(domain.indexOf(action.name)); } } } }; reducer = combineReducers(reducer); state = Immutable.fromJS(state); // Invoking CONSTRUCT to build the initial state. state = reducer(state, { name: 'CONSTRUCT' }); store = createStore(reducer, state); ``` ## Redux Reducer Composition Redux utilizes the concept of [reducer composition](http://gaearon.github.io/redux/docs/basics/Reducers.html#splitting-reducers). ```js let reducer = (state = {}, action) => ({ // <domain>: <domainReducer> (<domain data>, <action rel='nofollow' onclick='return false;'>) countries: countryReducer(state.countries, action), cities: cityReducer(state.cities, action) }); ``` The benefit of this pattern is that domain reducers do not need to know the complete state; domain reducers receive only part of the state for their domain. This enables better code separation. Redux [`combineReducers`](http://gaearon.github.io/redux/docs/api/combineReducers.html) is a helper that turns an object whose values are different reducing functions into a single reducing function. ```js let reducer = combineReducers({ countries: countryReducer, cities: cityReducer }); ``` However, Redux `combineReducers` does not dictate what should be the implementation of the domain reducer. Regardless of what is the implementation of the domain reducer, it does the same thing: listens to actions and when it recognizes an action, it create a new state, e.g. ```js export function countries (state = [], action) { switch (action.type) { case 'ADD_COUNTRY': // state = return state; case 'REMOVE_COUNTRY': // state = return state; default: return state; } } ``` There are several problems with this: * This makes the code base less standardized (across different projects and different developers). * Domain reducer function is called regardless of whether it can handle the action. * The overhead of maintaining the boilerplate. ## Validator Library Libraries that implement Canonical Reducer Composition pattern validation: * https://github.com/gajus/canonical-reducer-composition-validator ## Libraries Libraries that implement Canonical Reducer Composition: * https://github.com/gajus/redux-immutable
评论