feature-redux-logic is your feature-u integration point to
redux-logic! It promotes the logicAspect
(a feature-u
plugin) that facilitates redux-logic integration to your features.
Backdrop:
feature-u is a utility that facilitates feature-based project organization for your react project. It helps organize your project by individual features. feature-u is extendable. It operates under an open plugin architecture where Aspects integrate feature-u to other framework/utilities that match your specific run-time stack.
Overview:
feature-redux-logic configures redux-logic through the logicAspect
(which is supplied to feature-u's launchApp()
). This
extends feature-u's Feature
object by adding support for the
Feature.logic
property, referencing an array of feature-based logic modules.
It is important to understand that you continue to use redux-logic the same way you always have. It's just that now you are dealing with a smaller context ... within the boundaries of your feature!
Let's see how this all works together ...
-
peerDependencies ... you should already have these, because this is our integration point (but just in case):
npm install --save feature-u npm install --save redux-logic
-
the main event:
npm install --save feature-redux-logic
SideBar: Depending on how current your target browser is (i.e. it's JavaScript engine), you may need to polyfill your app (please refer to Potential Need for Polyfills).
-
Within your mainline, register the feature-redux-logic
logicAspect
to feature-u'slaunchApp()
(see:**1**
below).Please Note: redux must also be present in your run-time stack, because redux-logic is a middleware agent of redux (see:
**2**
below).src/app.js
import {launchApp} from 'feature-u'; import {createReducerAspect} from 'feature-redux'; // **2** import {createLogicAspect} from 'feature-redux-logic'; // **1** import features from './feature'; export default launchApp({ aspects: [ createReducerAspect(), // **2** createLogicAspect(), // **1** ... other Aspects here ], features, registerRootAppElm(rootAppElm) { ReactDOM.render(rootAppElm, getElementById('myAppRoot')); } });
-
Within each feature that maintains logic, simply register the feature's logic modules through the
Feature.logic
property (using feature-u'screateFeature()
) ... see:**3**
below.src/feature/myXyzFeature/index.js
import {createFeature} from 'feature-u'; import logic from './logic'; export default createFeature({ name: 'myXyzFeature', logic, // **3** myXyzFeature logic[] ... snip snip (other aspect properties here) });
-
As a convenience, feature-redux-logic auto injects the feature-u
Fassets object
as a dependency in your logic modules. This promotes full Cross Feature Communication.The following example, demonstrates the availability of the
fassets
named parameter:import {createLogic} from 'redux-logic'; export const someLogic = createLogic({ ... snip snip transform({getState, action, fassets}, next, reject) { ... fassets may be used for cross-feature-communication }, process({getState, action, fassets}, dispatch, done) { ... fassets may be used for cross-feature-communication } });
Well that was easy!! At this point redux-logic is completely setup for your application!
In the nutshell, that's a highlight of most everything you need to know to use feature-redux-logic! Go forth and compute!
Any feature that maintains business logic promotes it's own set of
logic modules through the Feature.logic
property (using
feature-u's createFeature()
). While these logic modules are
truly opaque assets (internal to the feature), they are of interest
to feature-redux-logic to the extent that they are needed to configure
redux-logic.
Because logic modules may require access to feature-u's Fassets object
during code expansion, this property can also be a
feature-u expandWithFassets()
callback (a function that
returns the logic modules) ... please refer to feature-u's
discussion of Managed Code Expansion.
It is important to understand that your interface to redux-logic has not changed in any way. In other words, you continue to use redux-logic the same way you always have. It's just that now you are dealing with a smaller context ... within the boundaries of your feature!
As always, you should strive to keep each feature isolated, so it is truly plug-and-play.
The featureName (i.e. Feature.name
) is a critical item that can be
used throughout your feature implementation to promote a consistent
feature identity.
A key aspect of the featureName is that feature-u guarantees it's uniqueness. As a result, it can be used to qualify the identity of several feature aspects.
A best practice in consideration of feature-based logic modules, is to prefix all logic module names with featureName. While this is not required, it helps (in diagnostic logs) to identify where that module lives.
Here is an example (see: **4**
):
src/feature/myXyzFeature/logic/doSomething.js
import {createLogic} from 'redux-logic';
import featureName from './featureName';
import actions from './actions';
export const doSomething = createLogic({
name: `${featureName}.doSomething`, // **4**
type: String(actions.some.action),
transform({getState, action}, next, reject) {
... snip snip
},
process({getState, action, fassets}, dispatch, done) {
... snip snip
}
});
There are many ways to introduce logic in your react app. This article breaks down the various options: Where do I put my business logic in a React-Redux application? The article is an introduction (and motivation) for the development of redux-logic ... redux middleware for organizing all your business logic.
I have been using redux-logic since it's inception and believe it is the best approach to encapsulate your business logic. Prior to redux-logic, my business logic was spread out in a variety of different places, including:
- component methods
- thunks
- and various middleware components
In addition, I relied heavily on batched actions, where logic entry points would stimulate multiple actions in one procedural chunk of code. Needless to say, this was less than ideal. Even tools like redux-dev-tools could not give me adequate insight into "what was stimulating what"!
All of these techniques were replaced with "true" business logic, organizing all my logic in one isolated spot, all orchestrated by redux-logic!
My business logic is now:
- located in one logical discipline (i.e. dedicated "logic" modules)
- making it testable in isolation (very nice)!!
- has more concise and succinct goals
- promotes modular reuse
- provides traceable "cause and effects"
- is greatly simplified!
feature-redux-logic accumulates all the logic modules from the various features of your app, and registers them to redux-logic. This is accomplished by creating the redux middleware which is in turn registered to redux. The Aspect Interface to this process (i.e. the inputs and outputs) are documented here.
- The input to feature-redux-logic are redux-logic modules.
This is specified by each of your features (that maintain logic)
through the
Feature.logic
property, containing a logic modules array.
-
The output from feature-redux-logic is the redux middleware component, exposed through the
Aspect.getReduxMiddleware()
API (an "aspect cross-communication mechanism"). This middleware component must be consumed by yet another aspect (such as feature-redux) that in turn manages redux. -
As a convenience, feature-redux-logic auto injects the feature-u
Fassets object
as a dependency in your logic modules. This promotes full Cross Feature Communication. Please refer to the Usage section for examples.
When feature-redux-logic detects that no logic modules have been specified by any of your features, it will (by default) throw the following exception:
***ERROR*** feature-redux-logic found NO logic modules within your features
... did you forget to register Feature.logic aspects in your features?
(please refer to the feature-redux-logic docs to see how to override this behavior).
Most likely this should in fact be considered an error (for example you neglected to specify the logic modules within your features). The reasoning is: why would you not specify any logic modules if your using redux-logic?
You can change this behavior through the following configuration:
logicAspect.config.allowNoLogic$ = true;
With this option enabled, when no logic modules are found, redux-logic will simply NOT be configured (accompanied with a WARNING logging probe).
You can also specify your own array of logic modules in place of the
true
value, which will be used ONLY in the scenario where no logic
modules were specified by your features.
-
Within your mainline, register the feature-redux-logic
logicAspect
to feature-u'slaunchApp()
. -
Within each feature that maintains business logic, simply register the feature's logic modules through the
Feature.logic
property (using feature-u'screateFeature()
).
API: createLogicAspect([name='logic']): logicAspect
The logicAspect
is the feature-u plugin that facilitates
redux-logic integration to your features.
To use this aspect:
Please refer to the Usage section for examples of this process.
The implementation of this library employs modern es2015+ JavaScript constructs. Even though the library distribution is transpiled to es5 (the least common denominator), polyfills may be required if you are using an antiquated JavaScript engine (such as the IE browser).
We take the approach that polyfills are the responsibility of the client app. This is a legitimate approach, as specified by the W3C Polyfill Findings (specifically Advice for library authors).
- polyfills should only be introduced one time (during code expansion of the app)
- a library should not pollute the global name space (by including polyfills at the library level)
- a library should not needlessly increase it's bundle size (by including polyfills that are unneeded in a majority of target environments)
As it turns out, app-level polyfills are not hard to implement, with the advent of third-party utilities, such as babel:
- simply import babel-polyfill
- or use babel's
babel-preset-env
in conjunction with babel 7's
"useBuiltins": "usage"
option
If your target JavaScript engine is inadequate, it will generate native run-time errors, and you will need to address the polyfills. Unfortunately, in many cases these errors can be very obscure (even to seasoned developers). The following Babel Feature Request (if/when implemented) is intended to address this issue.