Releases: jeffbski/redux-logic
v0.10.0
Possible breaking changes
It is unlikely that these changes would introduce problems in most codebases but since there is a possibility so I wanted to call them out.
- Several methods now throw errors if passed logic that is duplicated (same exact instance) since this is most likely a misconfiguration in your code. You can clone the logic with Object.assign or similar to create multiple instances if you really need to have the same logic multiple times in the stack.
createLogicMiddleware(arrLogic, deps)
- arrLogic is expected to have unique logic instanceslogicMiddleware.addLogic(arrLogic)
- arrLogic needs to be unique and not contain any instances that were already present in the logic stacklogicMiddleware.replaceLogic(arrLogic)
- arrLogic needs to be unique instances
- A new method is added which will check the array of logic and only merge in new items was added. This makes it easy to work with split bundles. If the logic already exists in the stack then it will be filtered out of the merge so only new items are added.
logicMiddleware.mergeNewLogic(arrLogic)
- will check whether logic already exists in the stack merging only the new ones into the stack.
- Use arity of process fn signature to determine the default dispatching mode (single, multiple, dispatchReturn) to make use more intuitive.
With v0.10.0 process has the following fn signature
process({ getState, action }, ?dispatch, ?done)
which results in the following three possible variations:
// dispatch from returned object, resolved promise, or observable
// this defaults processOptions.dispatchReturn = true enabling
// dispatching of the returned/resolved values
process({ getState, action }) {
return objOrPromiseOrObservable;
}
// single dispatch
process({ getState, action }, dispatch) {
dispatch(objOrPromiseOrObservable); // call exactly once
}
// multiple dispatch, call done when finished dispatching
// this defaults processOptions.dispatchMultiple = true
// which enables the multiple dispatch mode
process({ getState, action }, dispatch, done) {
dispatch(objOrPromiseOrObservable);
dispatch(objOrPromiseOrObservable);
done(); // call when done dispatching
}
Basically the arity of the function you provide for process determines the default values for a couple processOptions: dispatchReturn
and dispatchMultiple
. You can still set these values directly or just let them be inferred by the arity of process.
There is more discussion in the advanced section of the API docs.
Note: the previous v0.9 mechanism for multiple dispatches setting the second parameter of the dispatch function to {allowMore: true}
will also work but is deprecated in lieu of this simpler arity approach.
New features
- logic
name
property can be used to provide a meaningful name to logic. This property defaults toL(actionType)-idx
if one is not provided. Technically name was already available but it wasn't documented until now. processOptions.dispatchMultiple
- drives whether process is in single dispatch mode or multiple dispatch mode. When false, it is single dispatch mode, dispatch is expected to be called exactly once and when true, multi-dispatch mode is enabled where dispatch can be used any number of times until the done cb is called to complete. The default is driven by whether the process fn has thedone
parameter. See the advanced section of the API for more details.logicMiddleware.whenComplete(fn)
- for server-side use, logicMiddleware can let you know when all your dispatched actions and all their processing has completed so you can serialize the state and send it to the server.
// for server-side use, runs optional fn and returns promise
// when all in-flight processing/loading has completed.
// Use it after performing any required store.dispatch
store.dispatch(ROUTE_CHANGE); // triggers async loading in our logic
store.dispatch(FOO); // any number of additional dispatches
return logicMiddleware.whenComplete(() => { // returns promise
return store.getState(); // can serialize store, loading was done
});
logicMiddleware.monitor$
- observable for monitoring the internal operations inside all of your logic which is useful for debugging, testing, and for developing developer tools. More details in the advanced part of the API docs.
Migration instructions
- You can check that your createLogicMiddleware, addLogic, and replaceLogic don't try to add the same logic refs multiple times or just check for an error when you run your tests. It will indicate which logic instances are duplicates. If you really do need duplicates then make a clone with Object.assign or similar.
- If you are using
{ allowMore: true }
for multiple dispatching, it is recommended to switch to the more intuitive approach using the additionaldone
cb. The old way will likely continue to work since it is also used internally, but it is deprecated and you should change to the new style. Migrating involves taking out the{ allowMore: true}
and adding a call to thedone
cb when you are finished.
// since done is included in signature, multi-dispatch mode is enabled
// sets the default for processOptions.dispatchMultiple = true
process({ getState, action }, dispatch, done) {
dispatch(foo);
dispatch(bar); // dispatch any number of times
done(); // when done dispatching
}
- If you are using
processOptions.dispatchReturn = true
to dispatch the return obj, resolved promise or observable, then simply by omitting thedispatch
from your process fn you will set the default dispatchReturn to true. Thus you don't have to specifically set dispatchReturn to true. It is fine to leave the code in but it is unnecessary as long as your fn signature omits the dispatch.
processOptions: {
successType: FOO_SUCCESS, // optionally use this success action type/creator
failType: FOO_FAILED // optionally use this fail action type/creator
},
// simply by omitting dispatch, we set the default for dispatchReturn to true
process({ getState, action }) {
return objOrPromiseOrObservable; // returned or resolved value(s) will be dispatched
}
v0.9.3
v0.9.1
v0.9.0
Allow debounce, throttle, and latest to be used in combination. Original code would throw an error if debounce or throttle was used in conjunction with latest in logic, but now this is allowed.
Also I have reversed the order in which debounce and throttle are applied, first debouncing is applied, then throttling which I believe aligns best with most use cases.
v0.8.4
Clarify cancellation and take latest functionality and its relation to XHR abort in API doc. Add jsfiddle and full examples using RxJS Ajax which redux-logic can immediately abort on cancellation rather than simply suppressing the dispatch (as with http libraries that don't support in-flight cancellation).
v0.8.3
v0.8.2
v0.8.1
Note: use v0.8.2+ since the custom rxjs observable in this commit was missing the catch operator, fixed in 0.8.2
Added new option processOptions
which influences the behavior of the process hook. It basically allows additional declaration to eliminate additional clutter in your code.
If you set the processOptions
object, you can further influence how process behaves streamlining your code.
processOptions.dispatchReturn
- if true, then process will use the returned value to dispatch. If you return a promise then it will use the resolve/reject values for dispatching. If you return an observable then it will use its values or error for dispatching. Returning an undefined, promise that resolves to undefined, or observable value of undefined will cause no dispatch. Default is false.processOptions.successType
- if set to an action type string or an action creator function it will use this to create the action from the dispatched value. If thesuccessType
was a string then it will create an action of this type and set the payload to the dispatched value. If it was an action creator function then it will pass the value to the action creator and then dispatch that. Default undefined.processOptions.failType
- if set to an action type string or an action creator function it will use this to create the action from the dispatched error or rejected promise value or errored observable similar to howsuccessType
works. IffailType
is not defined and an error is thrown or dispatched that does not itself have atype
(action type), then an UNHANDLED_LOGIC_ERROR will be dispatched with the error as the payload. Default undefined.
An example of using processOptions
:
const logic = createLogic({
type: FOO,
processOptions: {
dispatchReturn: true, // use my return for dispatch
successType: 'FOO_SUCCESS', // my action type for success
failType: 'FOO_ERROR', // my action type for failure
},
process({ getState, action }) {
// no need to dispatch when using dispatchReturn: true
// actions are created from the resolved or rejected promise
return axios.get('https://server/api/users')
.then(resp => resp.data.users); // select the data
}
}
v0.8.0
Slight interface change, createLogic will now only accept either validate or transform since they now both refer to the same hook internally. Both aliases have been kept to help communicate the intent of the logic being used, though either can do the same things. So there are basically 2 hooks now validate/transfer and process. The internal code was greatly simplified in this change, I couldn't find a reason to justify the extra complexity. None of my non-test examples ever used both validate and transfer in the same logic so I doubt that this should be much of a problem. The migration path is simply to move all the logic into the validate or transform hook.
More documentation updates and a few jsfiddle async await examples and a full async await example were added.
v0.7.4
Minor internal refactoring to ensure no artificial delays were introduced by the validate and translate hooks. Synchronous code will stay synchronous. Added another jsbin example and a few other examples including a notification example. Added more to docs and started adding more jsdoc.