UNPKG

redux-composite

Version:
160 lines (105 loc) 6.09 kB
# Composite For low-level system the expression `Composite({reducer})` is the same as just `reducer`, because high-level composite resolves low-level reducers to `Composite` objects. High-level composite object is also instance of `Composite`. Also keep in mind, that `Structure(data)` is identical to `Composite({structure: data})`. Either `reducer` or `structure` should be provided to `Composite` constructor, otherwise you'll receive an exception. ## Injections There are 2 ways to initialize `Composite` object: providing structure of sub-systems or giving "primitives" (at least reducer) for a low-level system. If neither structure is provided nor reducer (for low-level system), the exception would be thrown. The list of injectable "terms": * reducer * middleware * equality * subscribe * redux * memoize The complete injection syntax: `Composite({structure, reducer, middleware, equality, subscribe, redux, memoize})` > **Note:** By default Redux strictly checks, that the `action` parameter [is plain object](https://github.com/reactjs/redux/blob/master/src/createStore.js#L166) and has `type` property: `action: {type: string}`. > If you're not using Redux or having custom middleware, dealing with you custom actions (like [Redux Thunk](https://github.com/gaearon/redux-thunk)), the type could be any. > So, let's say `type Action = action: {type: string} | any` ### Low-level injections The low-level composite initialization happens, when `structure` parameter is not provided to `Composite` constructor. For low-level system at least reducer **_is required_**. #### Reducer There is no default implementation for reducer. The reducer should accept `state` and `action` and return new state: `type Reducer = (state: State, action: Action): State` Keep your reducers immutable! Do not mutate original state - return new object, if state change. Otherwise, equality check may fail - and other features (memoize, subscribe) may not work. #### Middleware Default: `() => next => action => next(action)` You can inject any middleware, that follows interface: ```js type Middleware = ({dispatch: (action: Action) => Action, getState: () => State}): ((action: Action) => Action). ``` #### Equality Default: `(prev, next) => prev === next` - let's call it "default equality" If you have more complex equality check between previous and next state, inject your own implementation: `type Equality = (prev: State, next: State): boolean` #### Subscribe Default: `Wrappers.Subscribe((dispatch, getState) => listener => () => listener({dispatch, getState}), equality)` If you want additional custom behavior on all subscribed listeners to you low-level state changes, inject it using this interface: ```js type Subscribe = (dispatch: (action: Action) => Action, getState: () => State): (Function => () => any) ``` `Wrappers.Subscribe` (based on given equality) checks the state changes and execute listener only in case, when state was changed. So, you may also want to wrap your subscribe injection with `Wrappers.Subscribe` > If equality is not provided, default equality will be used #### Redux Default: ```js (dispatch, getState, subscribe) => ({ redux: {dispatch, getState, subscribe} }) ``` By default the library provides in `redux` property the same low-level methods, as it was transformed from the high-level ones. You can implement your own wrappers on transformed methods using the interface: ```js type Redux = ( dispatch: (action: Action) => Action, getState: () => State, subscribe: (Function => any)): {redux: { dispatch: (action: Action) => Action, getState: () => State, subscribe: (Function => any) }, structure?: mixed} ``` #### Memoize Default: `Wrappers.Memoize(getState => ({memoize: callback => callback}), equality)` You're free to implement your own, using the interface: `memoize(getState: () => State): {memoize: Function => Function}` `Wrappers.Memoize` (based on given equality) checks the stage changes and caches result of the callback. So, you may also want to wrap your memoize injection with `Wrappers.Memoize` > If equality is not provided, default equality will be used ### Composite injections If `structure` is provided, library assumes that this is not low-level component, therefore all the injections receive the transformed `structure`. The transformation converts all the low-system reducers to `Composite` objects. If low-system already provided as `Composite` object, no transformation happens. If you have nothing to inject, except `structure`, you can use short syntax to create `Composite` object: `Structure(highLevelSystemStructure)`, which is alias for `Composite({structure: highLevelSystemStructure})`. All the default implementations for high-level "terms" could be imported in `Defaults` object. #### Reducer Default: `Defaults.Reducer` Interface: `(structure: mixed): Reducer` #### Middleware Default: `Defaults.Middleware` Interface: `(structure: mixed): Middleware` #### Equality Default: `Defaults.Equality` Interface: `(structure: mixed): Equality` #### Subscribe Default: `structure => Wrappers.Subscribe(Defaults.Subscribe(structure), equality)` `Wrappers.Subscribe` (based on given equality) checks the state changes and execute listener only in case, when state was changed. So, you may also want to wrap your subscribe injection with `Wrappers.Subscribe` the same way as `Defaults.Subscribe` > If equality is not provided, default equality will be used Interface: `(structure: mixed): Subscribe` #### Redux Default: `Defaults.Redux` Interface: `(structure: mixed): Redux` #### Memoize Default: `structure => Wrappers.Memoize(Defaults.Memoize(structure), equality)` `Wrappers.Memoize` (based on given equality) checks the stage changes and caches result of the callback. So, you may also want to wrap your memoize injection with `Wrappers.Memoize` the same way as `Defaults.Memoize` > If equality is not provided, default equality will be used Interface: `(structure: mixed): Memoize` Read next: [createStore](create-store.md)