@duckness/pool
Version:
Duckness Pool - @duckness/duck + Redux
371 lines (285 loc) • 13.7 kB
Markdown
# `/pool` <!-- omit in toc -->
[/duck](https://github.com/hitosu/duckness/tree/master/packages/duck) + [Redux](https://redux.js.org/)
[](https://www.npmjs.com/package/@duckness/pool)
[](https://github.com/hitosu/duckness/blob/master/LICENSE)
[](https://www.npmjs.com/package/@duckness/pool?activeTab=dependencies)
[](https://github.com/hitosu/duckness/issues)
[](https://github.com/hitosu/duckness/issues)
[](https://www.npmjs.com/package/@duckness/pool)
# Example
```js
import Pool from '/pool'
import CounterDuck from './ducks/CounterDuck'
const CounterPool = Pool({
buildStore: ({ initialCounter = 0 } = {}) => {
return { counter: initialCounter }
}
})
CounterPool.addDuck(CounterDuck)
CounterPool.build({initialCounter: 0})
CounterPool.store
// => [redux store]
```
# Table of Contents <!-- omit in toc -->
- [Example](#example)
- [API](#api)
- [Create Pool](#create-pool)
- [`buildStore`](#buildstore)
- [default `buildStore`](#default-buildstore)
- [`buildRootReducer`](#buildrootreducer)
- [default `buildRootReducer`](#default-buildrootreducer)
- [`.addDuck(duck)`](#addduckduck)
- [`.preReducer(reducer)`](#prereducerreducer)
- [`.postReducer(reducer)`](#postreducerreducer)
- [`.build(props)`](#buildprops)
- [`.store`](#store)
- [`.dispatch(...)`](#dispatch)
- [`.reduce([state,] action)`](#reducestate-action)
- [`.select(?selector)`](#selectselector)
- [`.fetch(selector, ?resolver)`](#fetchselector-resolver)
- [`.trigger(selector, callback, ?resolver)`](#triggerselector-callback-resolver)
- [`.setErrorReporter(reporterFn)`](#seterrorreporterreporterfn)
- [`.reportError(error)`](#reporterrorerror)
- [`.addMiddleware(middleware)`](#addmiddlewaremiddleware)
- [Pool Streams - Pool plugins](#pool-streams---pool-plugins)
- [`.addStream(poolStream)`](#addstreampoolstream)
- [`PoolStream` API](#poolstream-api)
- [`.ducks`](#ducks)
- [`.getDuckByName(duckPath)`](#getduckbynameduckpath)
- [`duckPath`:](#duckpath)
- [`.middlewares`](#middlewares)
- [`.streams`](#streams)
- [`.props`](#props)
- [Examples](#examples)
- [ packages:](#duckness-packages)
# API
## Create Pool
```js
const myPool = Pool({
?poolName: String, // pool name, default is 'pool'
?ducks: Array<Duck>, // array of pool ducks
?middlewares: Array<Middleware>, // additional middlewares for Redux store
?streams: Array<PoolStream>, // pool plugins
?buildStore: (props, { refProps, refDucks, refErrorReporter }) => storeState, // build initial store state
?buildRootReducer: ({ refDucks, refErrorReporter }) => rootReducer // build custom root reducer from ducks instead of default root reducer
})
```
### `buildStore`
Optional function to build init store state
`(props, { refProps, refDucks, refReducers, refErrorReporter }) => storeState`
* `props` are passed to pool when `pool.build(props)` are called.
* `refProps.current` - props passed to `pool.build(props)` function
* `refDucks.current` - current array of ducks.
* `refReducers.root` - pool root reducer
* `refReducers.pre` - pool pre reducer
* `refReducers.post` - pool post reducer
* `refErrorReporter.current` - current error reporter function.
#### default `buildStore`
If not specified store state will be set to `{}`.
### `buildRootReducer`
Optional function for custom root reducer
`(ducks, { refProps, refDucks, refReducers, refErrorReporter }) => rootReducer`
* `ducks` - array of ducks
* `refProps.current` - props passed to `pool.build(props)` function
* `refDucks.current` - current array of ducks.
* `refReducers.pre` - pool pre reducer
* `refReducers.post` - pool post reducer
* `refErrorReporter.current` - current error reporter function.
#### default `buildRootReducer`
Default root reducer will combine all duck root reducers via
```js
ducks.reduce((state, duck) => {
return duck(state, action)
}, state)
```
with every duck root reducer wrapped in try/catch returning unmodified state in case of exception inside duck root reducer.
## `.addDuck(duck)`
Add duck to pool.
```js
MyPool.addDuck(myDuck)
```
## `.preReducer(reducer)`
Add pool pre reducer.
Pre reducers will run BEFORE duck reducers and will receive all actions.
Will replace previously set pre reducer.
```js
MyPool.preReducer(function globalPreReducer(state, action) {/* ... */} )
```
## `.postReducer(reducer)`
Add pool post reducer.
Post reducers will run AFTER duck reducers and will receive all actions.
Will replace previously set post reducer.
```js
MyPool.postReducer(function globalPostReducer(state, action) {/* ... */} )
```
## `.build(props)`
Build pool state from some props. `props` will be passed to `buildStore` function.
```js
MyPool.build({ initialCounter: 0 })
```
## `.store`
Reference to built redux store.
```js
MyPool.store.subscribe(/* ... */)
```
## `.dispatch(...)`
1. Dispatches an action (action is a plain object).
```js
const action = { type: 'actionType', payload: {} }
MyPool.dispatch(action)
```
2. Dispatches and action from 'poolName/duckName' duck.
This will first look for a duck by 'duckName' and current 'poolName', then duck's action creator by 'actionName' and call it with 'actionParams' to build action to dispatch.
```js
MyPool.dispatch('duckName', 'actionName', ...actionParams)
```
Example:
```js
const MyDuck = Duck('myDuck', 'myPool')
MyDuck.action('someAction')
const MyPool = Pool({ poolName: 'myPool' })
MyPool.addDuck(MyDuck)
MyPool.dispatch('myDuck', 'someAction', ...actionParams)
// equal to
MyPool.dispatch(MyDuck.action.someAction(...actionParams))
```
3. Dispatches and action from 'customPoolName/duckName' duck.
This will first look for a duck by 'duckName' and 'poolName', then duck's action creator by 'actionName' and call it with 'actionParams' to build action to dispatch.
```js
MyPool.dispatch(['customPoolName', 'duckName'], 'actionName', ...actionParams)
```
## `.reduce([state,] action)`
Reduce state with action. Does not change pool's store. Can be used to look ahead what the state will be after dispatching an action.
```js
MyPool.reduce(action) // reduce pool's store state
MyPool.reduce(state, action) // reduce custom state
```
## `.select(?selector)`
Select something from store. Returns store state if `selector` undefined.
```js
MyPool.select(MyDuck.select.something)
// => selected value
MyPool.select()
// => current store state
```
## `.fetch(selector, ?resolver)`
Returns `Promise`
`function resolver(selected, resolve, prevSelected)`
Fetch value from the store:
- first `selector` is called to produce selected value
- if `resolver` is defined: selected value is passed to resolver with previous selected value (`undefined` for the first run) and `resolve` function that can be used to resolve this `fetch`
- if `resolver` is not defined: selected value will be resolved if it is not `undefined`
- if nothing was resolved: repeat when store is updated (after next action dispatch).
`fetch` will subscribe to store updates (if it was not resolved after first select) and unsubscribe when it is resolved.
```js
// resolves with items when items are loaded into store or are already present
const items = await ListPool.fetch(ItemsDuck.select.items)
// resolves when counter is reset, returning counter value before reset
const counterResetFrom = await MyPool.fetch(
state => state.counter,
(selected, resolve, prevSelected) => {
if (null != prevSelected && 0 !== prevSelected && 0 === selected) {
resolve(selected)
}
}
)
```
## `.trigger(selector, callback, ?resolver)`
Returns clear trigger function.
`function callback(selected)`
`function resolver(selected, resolve, prevSelected)`
Trigger `callback` every time:
- selected value changes (`resolver` is undefined)
- or `resolver` calls `resolve`
`trigger` calls `resolver` first time trigger is set and later on store updates.
To unsubscribe call clear trigger function.
```js
// console.log value for 10 seconds
const clearTrigger = MyPool.trigger(
MyDuck.select.someValue,
console.log
)
setTimeout(clearTrigger, 10000)
```
## `.setErrorReporter(reporterFn)`
Set exception reporter function. Will also overwrite errorReporters set in ducks.
```js
MyPool.setErrorReporter(error => {
window.Sentry.captureException(error)
})
```
## `.reportError(error)`
Call assigned error reporter
```js
MyPool.reportError(new Error('Clean pool!'))
```
## `.addMiddleware(middleware)`
Add custom Redux middleware (applied on `.build`)
## Pool Streams - Pool plugins
Pool Stream is a Pool plugin that can add middlewares to Redux store. One example is [/pool-saga-stream](https://github.com/hitosu/duckness/tree/master/packages/pool-saga-stream) that adds [@duckness/saga](https://github.com/hitosu/duckness/tree/master/packages/saga) support to Pool (ducks can have [Redux Sagas](https://redux-saga.js.org/)).
### `.addStream(poolStream)`
Adds Pool Stream to Pool
### `PoolStream` API
```js
{
// array of middlewares for Redux store
middlewares({ refDucks, refProps, refReducers, refErrorReporter }) {
// refDucks.current - current array of ducks
// refProps.current - props passed to build function
// refReducers.root - root reducer function
// refReducers.pre - pool pre reducer function
// refReducers.post - pool post reducer function
// refErrorReporter.current - current error reporter function
// ...
return [...streamMiddlewares]
},
// called before redux store is built
beforeBuild({ refDucks, refProps, refReducers, refErrorReporter }) {
// refDucks.current - current array of ducks
// refProps.current - props passed to build function
// refReducers.root - root reducer function
// refReducers.pre - pool pre reducer function
// refReducers.post - pool post reducer function
// refErrorReporter.current - current error reporter function
// ...
},
// called after redux store is built
afterBuild({ refStore, refDucks, refProps, refReducers, refErrorReporter }) {
// refStore.current - current Redux store
// refDucks.current - current array of ducks
// refProps.current - props passed to build function
// refReducers.root - root reducer function
// refReducers.pre - pool pre reducer function
// refReducers.post - pool post reducer function
// refErrorReporter.current - current error reporter function
// ...
}
}
```
## `.ducks`
Array of added ducks (read only)
## `.getDuckByName(duckPath)`
Find added duck by it's path. Returns `null` if duck is not found.
### `duckPath`:
* Array: `[poolName, duckName]`
* String: `duckName` (use current poolName)
## `.middlewares`
Array of added middlewares (read only)
## `.streams`
Array of added streams (read only)
## `.props`
Props used to build pool (read only)
# Examples
https://github.com/hitosu/duckness/tree/master/stories
# packages:
* [/duck](https://github.com/hitosu/duckness/tree/master/packages/duck) - [Modular Redux Ducks](https://github.com/erikras/ducks-modular-redux) hatchery
* [/saga](https://github.com/hitosu/duckness/tree/master/packages/saga) - [Redux Saga](https://redux-saga.js.org/) extension for [/duck](https://github.com/hitosu/duckness/tree/master/packages/duck)
* [/epic](https://github.com/hitosu/duckness/tree/master/packages/epic) - [Redux-Observable](https://redux-observable.js.org/) extension for [/duck](https://github.com/hitosu/duckness/tree/master/packages/duck)
* [/pool](https://github.com/hitosu/duckness/tree/master/packages/pool) - [@duckness/duck](https://github.com/hitosu/duckness/tree/master/packages/duck) + [Redux](https://redux.js.org/)
* [/pool-saga-stream](https://github.com/hitosu/duckness/tree/master/packages/pool-saga-stream) - [@duckness/saga](https://github.com/hitosu/duckness/tree/master/packages/saga) plugin for [@duckness/pool](https://github.com/hitosu/duckness/tree/master/packages/pool)
* [/pool-epic-stream](https://github.com/hitosu/duckness/tree/master/packages/pool-epic-stream) - [@duckness/epic](https://github.com/hitosu/duckness/tree/master/packages/epic) plugin for [@duckness/pool](https://github.com/hitosu/duckness/tree/master/packages/pool)
* [/react-redux-pool](https://github.com/hitosu/duckness/tree/master/packages/react-redux-pool) - [@duckness/pool](https://github.com/hitosu/duckness/tree/master/packages/pool) + [React-Redux](https://react-redux.js.org/)
* [/use-redux](https://github.com/hitosu/duckness/tree/master/packages/use-redux) - [React hook](https://reactjs.org/docs/hooks-intro.html) for [Redux](https://react-redux.js.org/) store
* [/use-pool](https://github.com/hitosu/duckness/tree/master/packages/use-pool) - [React hook](https://reactjs.org/docs/hooks-intro.html) for [@duckness/pool](https://github.com/hitosu/duckness/tree/master/packages/pool).
* [/store](https://github.com/hitosu/duckness/tree/master/packages/store) - simple store for [React](https://reactjs.org/) components
* [/reactor](https://github.com/hitosu/duckness/tree/master/packages/reactor) - reactive data flow builder