Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/cyrilluce/saga-duck
extensible and composable duck for redux-saga
https://github.com/cyrilluce/saga-duck
ducks-pattern redux redux-saga saga-duck typescript
Last synced: 11 days ago
JSON representation
extensible and composable duck for redux-saga
- Host: GitHub
- URL: https://github.com/cyrilluce/saga-duck
- Owner: cyrilluce
- License: mit
- Created: 2017-06-15T07:56:47.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2023-03-04T04:23:33.000Z (over 1 year ago)
- Last Synced: 2024-10-26T03:06:20.292Z (17 days ago)
- Topics: ducks-pattern, redux, redux-saga, saga-duck, typescript
- Language: TypeScript
- Size: 913 KB
- Stars: 98
- Watchers: 5
- Forks: 15
- Open Issues: 15
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# saga-duck
extensible and composable duck for redux-saga, **typescript 3.x supported**.See also
[ducks-modular-redux](https://github.com/erikras/ducks-modular-redux)
[extensible-duck](https://github.com/investtools/extensible-duck)# example
Run command:
```sh
npm start
```
![example of Counters](examples/example.gif)
Source code are in `examples` directory# usage
## install
```sh
npm i saga-duck -S
```## Documents
[Document-中文](https://cyrilluce.gitbook.io/saga-duck/)for 2.x please visit
[Legacy Document-中文](https://cyrilluce.gitbooks.io/saga-duck)## memtion
Ducks should be stateless, so we can use React FSC(functional stateless compoment) and optimize later.
You should only access store by duck.selector or duck.selectors.## single duck
```js
import { Duck } from "saga-duck";
import { takeEvery, call, put, select } from "redux-saga/effects";
import { delay } from "redux-saga";class SingleDuck extends Duck {
get quickTypes() {
return {
...super.quickTypes,
INCREMENT: 1,
INCREMENT_ASYNC: 1
};
}
get reducers() {
const { types } = this;
return {
...super.reducers,
count: (state = 0, action) => {
switch (action.type) {
case types.INCREMENT:
return state + 1;
default:
return state;
}
}
};
}
*saga() {
yield* super.saga();
const { types, selector } = this;
yield takeEvery(types.INCREMENT_ASYNC, function*() {
yield call(delay, 1000);
// select state of this duck
const { count } = selector(yield select());
yield put({type: types.INCREMENT});
});
}
}
```## extend duck
```js
class ExtendedDuck extends SingleDuck {
get quickTypes(){
return {
...super.quickTypes,
MORE: 1
}
}
get reducers(){
return {
...super.reducers,
more: (state, action) => 1
}
}
get rawSelectors(){
return {
...super.rawSelectors,
more(state){
return state.more
}
}
}
get creators(){
const { types } = this
return {
...super.creators,
more(){
return {
type: types.MORE
}
}
}
}
*saga(){
yield* super.saga()
const { types, selector, selectors, creators } = this
yield take([types.INCREMENT, types.MORE])
const { count, more } = selector(yield select())
const _more = selectors.more(yield select())
yield put(creators.more())
}
}
```## compose ducks
```js
import { ComposableDuck } from "saga-duck";class ComposedDuck extends ComposableDuck {
get quickTypes() {
return {
...super.quickTypes,
PARENT: 1
};
}
get quickDucks() {
return {
...super.quickDucks,
duck1: SingleDuck,
duck2: ExtendedDuck,
duck3: ExtendedDuck
};
}
*saga() {
yield* super.saga();
const {
types,
selector,
ducks: { duck1, duck2, duck3 }
} = this;
yield takeEvery(types.PARENT, function*() {
yield put({ type: duck1.types.INCREMENT });
yield put(duck2.creators.more());
yield put(duck3.creators.more());
});
// { parent, duck1: {count}, duck2: {count, more}, duck3: {count, more} }
const state = selector(yield select());
}
}
```## Run and connect to React (Legacy style)
```js
import { DuckRuntime } from "saga-duck";
import Root from "./Root";
import Duck from "./RootDuck";const duckRuntime = new DuckRuntime(new Duck({...}));
const ConnectedComponent = duckRuntime.connectRoot()(Root);ReactDOM.render(
,
document.getElementById("root")
);
```
Root.ts (Duck Component)
```js
import * as React from 'react'
import Counter from "./Counter";
import { DuckCmpProps } from 'saga-duck';
import Duck from './RootDuck'export default function Root({ duck, store, dispatch }: DuckCmpProps) {
const { selectors, creators, ducks: { counter1 } } = duck;
return (
counter1:
myself: total increment times: {selectors.total(store)}
dispatch(creators.increment())}>
Increment all
);
}
```## Run and connect to React (hook style)
```js
import * as React from 'react'
import Counter from "./Counter";
import Duck from './RootDuck'
import { useDuck } from 'saga-duck'export default function Root() {
const { duck, store, dispatch } = useDuck(Duck)
const { selectors, creators, ducks: { counter1 } } = duck;
return (
counter1:
myself: total increment times: {selectors.total(store)}
dispatch(creators.increment())}>
Increment all
);
}
```## Helpers
### useDuck
Connect duck to react in hook style.
```js
function MyCmp(){
const { duck, store, dispatch } = useDuck(MyDuck)
const { selector, creators } = duck;
return <>{selector(store).xxx}>
}
```
### purify
make React DuckComponent pure, only rerender when props and duck state changed.
```javascript
import { purify } from 'saga-duck'
export default purify(function DuckComponent({ duck, store, dispatch }){ ... })
```### memorize
stabilize objects/functions reference, prevent React props unnecessary change.**With React 16, you can use `useMemo` or `useCallback` hooks instead**
```javascript
import { memorize } from 'saga-duck'
const getHandler = memorize((duck, dispatch) => ()=>dispatch(duck.creators.bar()) )function Container(props){
const handler = gethandler(props)
return
}
```### reduceFromPayload / createToPayload
Create simple reducer / actionCreator
```javascript
import { reduceFromPayload, createToPayload } from 'saga-duck'let reducer = reduceFromPayload(types.SET_ID, 0)
// equal to
reducer = (state=0, action)=>{
switch(action.type){
case types.SET_ID:
return action.payload
default:
return state
}
}let creator = createToPayload(types.SET_ID)
// equal to
creator = (id: number)=>({ type: types.SET_ID, payload: id })
```## Typescript support
See [Duck example](./examples/src/CounterDuck.ts) and [ComposableDuck example](./examples/src/RootDuck.ts), please use typescript **3.0+** for saga-duck 3.x, and typescript 2.6.1 for saga-duck 2.x.
### types
![ts hint of types](examples/ts-types.png)### state (generate from reducers)
![ts hint of state](examples/ts-state.png)### selectors
![ts hint of selectors](examples/ts-selectors.png)