extensible-duck
extensible-duck is an implementation of the Ducks proposal. With this library you can create reusable and extensible ducks.
- Basic Usage
- Creating Reusable Ducks
- Extending Ducks
- Creating Reusable Duck Extensions
- Creating Ducks with selectors
Basic Usage
// widgetsDuck.js
// reducers.js widgetDuckstore: widgetDuckreducer
Constructor Arguments
const { namespace, store, types, consts, initialState, creators } = options
Name | Description | Type | Example |
---|---|---|---|
namespace | Used as a prefix for the types | String | 'my-app' |
store | Used as a prefix for the types and as a redux state key | String | 'widgets' |
storePath | Object path of the store from root infinal redux state. Defaults to the [duck.store] value. Can be used to define duck store location in nested state | String | 'foo.bar' |
types | List of action types | Array | [ 'CREATE', 'UPDATE' ] |
consts | Constants you may need to declare | Object of Arrays | { statuses: [ 'LOADING', 'LOADED' ] } |
initialState | State passed to the reducer when the state is undefined | Anything | {} |
reducer | Action reducer | function(state, action, duck) | (state, action, duck) => { return state } |
creators | Action creators | function(duck) | duck => ({ type: types.CREATE }) |
sagas | Action sagas | function(duck) | duck => ({ fetchData: function* { yield ... } |
takes | Action takes | function(duck) | duck => ([ takeEvery(types.FETCH, sagas.fetchData) ]) |
selectors | state selectors | Object of functions or function(duck) |
{ root: state => state} or duck => ({ root: state => state }) |
Duck Accessors
- duck.store
- duck.storePath
- duck.reducer
- duck.creators
- duck.sagas
- duck.takes
- duck.selectors
- duck.types
- for each const, duck.<const>
Helper functions
- constructLocalized(selectors): maps selectors syntax from
(globalStore) => selectorBody
into(localStore, globalStore) => selectorBody
.localStore
is derived fromglobalStore
on every selector execution usingduck.storage
key. Use to simplify selectors syntax when used in tandem with reduxes'combineReducers
to bind the duck to a dedicated state part (example). If defined will use the duck.storePath value to determine the localized state in deeply nested redux state trees.
Defining the Reducer
While a plain vanilla reducer would be defined by something like this:
{ }
Here the reducer has two slight differences:
- It receives the duck itself as the third argument
- It doesn't define the initial state (see Defining the Initial State)
// ... { }
With the duck
argument you can access the types, the constants, etc (see Duck Accessors).
Defining the Creators
While plain vanilla creators would be defined by something like this:
{ return type: CREATE widget } // Using thunk { return { }}
With extensible-duck you define it as an Object of functions:
If you need to access any duck attribute, you can define a function that returns the Object of functions:
Defining the Sagas
While plain vanilla creators would be defined by something like this:
{ try const payload = catcherr } // Defining observer
With extensible-duck you define it as an Object of functions accessing any duck attribute:
Defining the Initial State
Usually the initial state is declared within the the reducer declaration, just like bellow:
{ // ...}
With extensible-duck you define it separately:
If you need to access the types or constants, you can define this way:
Defining the Selectors
Simple selectors:
Composed selectors:
Using with Reselect:
Selectors with duck reference:
Defining the Types
Defining the Constants
Creating Reusable Ducks
This example uses redux-promise-middleware and axios.
// remoteObjDuck.js { return namespace store consts: statuses: 'NEW' 'LOADING' 'READY' 'SAVING' 'SAVED' types: 'UPDATE' 'FETCH' 'FETCH_PENDING' 'FETCH_FULFILLED' 'POST' 'POST_PENDING' 'POST_FULFILLED' { } type: typesUPDATE payload: fields type: typesFETCH payload: axios type: typesPOST payload: axios type: typesPATCH payload: axios obj: initialState || {} status: statusesNEW entities:
// usersDuck.js namespace: 'my-app' store: 'user' path: '/users'
// reducers.js userDuckstore: userDuckreducer
Extending Ducks
This example is based on the previous one.
// usersDuck.js namespace: 'my-app'store: 'user' path: '/users'
Creating Reusable Duck Extensions
This example is a refactor of the previous one.
// resetDuckExtension.js types: 'RESET' { type: typesRESET payload: fields }
// userDuck.js namespace: 'my-app'store: 'user' path: '/users'
Creating Ducks with selectors
Selectors help in providing performance optimisations when used with libraries such as React-Redux, Preact-Redux etc.
// Duck.js
// reducers.js Duckstore: Duckreducer
// HomeView.js @Component { // make use of sliced state here in props ... }