Redux with less boilerplate, actions statuses and controlled side-effects in a single shot.
- no more action creators and action types, just actions¹ and reducers
- actions dispatch their result automatically
- error status for every action with no extra code
- busy status for every async action (yep, no extra code!)
- cancellable actions
¹ With Actionware, actions have a different meaning: they're just functions which execution generate events. See usage section to better understand.
Extra power
Wanna have state selectors/getters in a decent way? Use it combined with Stateware lib.
Setup
Install it
- Yarn:
yarn add actionware
- NPM:
npm i actionware --save
After creating your Redux store, let Actionware know your store instance. Optionally you
can define custom action types prefix and suffixes:
; actionware;
Add actionware reducer to your root reducer:
To make Redux store react to busy and error status changes, make sure you add the Actionware reducer into your root reducer.
;; const rootReducer = ;
Usage
Simple actions
{ }
Async actions
Whatever you return will be the action payload
// Note that the store is always the last arg { const response = await ; return response;}
Invoke any action
Use call
to invoke an action and let Actionware handle
the execution lifecycle (managing error and busy statuses, notifying listeners, etc).
; ;
Cancel an action execution
; const actionCall = ; actionCall
To cancel inner calls or other async executions, use setExtra
inside an async action
to keep information needed and use them on a cancellation listener:
;; // Don't use arrow functions here, // otherwise a context value can't be set { const apiCall = api const anotherActionCall = this this // you can call it multiple times const apiResponse = await apiCall const anotherResponse = await anotherActionCall // ... return apiResponsedata} { // ...}
Clear action error
{ // ...}
Reducers:
;; const initialState = users: count: 0 ; initialState // Bind legacy action types // Bind multiple actions to the same handler // Actionware handles errors, cancellation and 'before' events, // but if you need to do something else ;
Busy and failure statuses for all your actions:
;; // Whenever needed...;;
Use listeners to manage side effects:
Note that busy listeners are called when busy status changes.
;; // global success listener; // per action success listener; // error listeners;; // cancellation listeners;; // before listeners // NOTE: 'beforeAll' is just an alias for 'before';;
Interaction-dependent flows
When you have "complex" flows that depend on some interaction to start or continue,
you can use next
to wait for some action completion in this fashion:
;; { // Wait for the next successful login await ; ; await ; history; ; await ;} // At some point, start the flow;
Usage with React
Inject actions and status into components as props
By using withActions
to wrap a component, actions are injected into it as props
and can be invoked without using call
.
;;;; const actions = loadUsers ; const mapStateToProps = users : companyusers loading : error : ; @@ { thisprops; } { const loading error = thisprops; if loading return <div>Loading...</div>; if error return <div>Failed to load users...</div>; return <div> users </div> ; }
Without injecting actions as props
In case you prefer not injecting actions as props into your component, you can use createActions
this way:
const actions = const MyComponent = <div> <button onClick= actionssomeAction ></button> </div>
Testing
call
and next
functions
Mock While testing, you're able to replace the call
and next
functions by custom
spy/stub to simplify tests.
; const callSpy = sinon;const nextStub = sinon; ;; // Get back to default behavior; ;
Reducers
For testing reducers, you can do the following:
;;; ;
API
Setup
- setup({ store, defaultPrefix?, errorSuffix?, busySuffix?, cancelSuffix? }): void
Most used
- withActions(actions: object): Function(wrappedComponent: Component)
- createActions(actions: object): object
- isBusy(action: Function): bool
- getError(action: Function): object
- clearError(action: Function): void
- call(action: Function, ...args)
- next(action: Function)
- createReducer(initialState: object, handlers: []): Function
Listeners
Global
- onSuccess(listener: ({ action, payload, args, store }) => void)
- onError(listener: ({ action, error, args, store }) => void)
- beforeAll(listener: ({ action, args, store}) => void)
Per action
- onSuccess(action: Function, listener: ({ payload, args, store }) => void)
- onError(action: Function, listener: ({ error, args, store }) => void)
- before(action: Function, listener: ({ args, store }) => void)
Test helpers
- mockCallWith(fakeCall: Function)
- mockNextWith(fakeNext: Function)
- successType(action: Function)
- errorType(action: Function)
- busyType(action: Function)
License
MIT © Wellington Guimaraes