# 介绍&入门

## saga-duck是用来做什么的？

一 句话：它是基于[ducks模式](https://github.com/erikras/ducks-modular-redux)思想，实现了模块化、可复用、可扩展及**可组合**特性的[redux-saga](https://github.com/redux-saga/redux-saga)开发方案。

在使用[Redux](https://github.com/reactjs/redux)时，我们发现业务逻辑过于分散，没有模块化，于是决定使用ducks模式来管理代码。

因为业务交互复杂，使用了redux-saga来管理逻辑，这就需要duck能支持redux-saga，并可以扩展和组合使用。[extensible-duck](https://github.com/investtools/extensible-duck) 是个很不错的方案，但是它没有考虑支持redux-saga，并且组合使用不太方便，于是我们便重新造一个轮子。

### 实现可复用

通常Redux的actionType都定义为常量，是固定不变的。但如果要复用，在同一个Redux store中就会冲突了，于是和extensible-duck一样最终的duck实例会根据路径的不同，生成不同的actionType（例如 namespace/route/ADD）。

actionType不同了，理所当然相关的reducer和actionCreator也会不同，都是动态生成。

```javascript
import {Duck} from 'saga-duck'
class MyDuck extends Duck{
    // duck.types会从它来自动生成
    get quickTypes(){
        return {
            ...super.quickTypes,
            ADD: 1
        }
    }
    get reducers(){
        // types是由quickTypes自动生成的，如果使用typescript，会自动进行提示
        const { types } = this
        return {
            ...super.reducers,
            num: (state=0, action)=>{
                switch(action.type){
                    case types.ADD:
                        return state+1
                    default:
                        return state
                }
            }
        }
    },
    get creators: (){
        return {
            ...super.creators,
            add(){
                return {type: types.ADD}
            }
        }
    }
}
```

### 支持redux-saga

&#x20;在参考extensible-duck后，我们添加了可扩展的saga和selectors

### saga

saga-duck 3.x中，duck自身有一个generator成员\`saga\`，可以直接扩展它

### selector/selectors

&#x20;通常我们都是比较暴力地访问store，而在模块化后，你并不确定Duck被用在哪个位置，所以在saga中访问状态需要通过selector/selectors进行

```typescript
const state = duck.selector(yield select()) // 获取当前duck对应的state
const xxx = duck.selectors.xxx(yield select()) // 获取对应的selectors的值

// 注意，这里我们没有用下面被注释掉的写法（它才是redux-saga推荐的）
// 是因为typescript的限制，在yield后我们丢失了类型信息。
// 用上面的写法，所有类型都在，可以方便地进行编码。

// yield select(duck.selector)
// yield select(duck.selectors.xxx)
```

&#x20;于是完整的带saga逻辑的duck就是这样

```typescript
import {Duck} from 'saga-duck'
class MyDuck extends Duck{
    get quickTypes...
    get reducers...
    get creators...
    get rawSelectors(){
        return {
            ...super.rawSelectors(),
            num: state => state.num
        }
    }
    *saga(){
        yield* super.saga()
        const {types, selectors, selector, creators} = this
        yield takeEvery(types.ADD, function*(add){
            const state = selector(yield select()) // { num: 1 }
            const num = selectors.num(yield select()) // 1
            yield put(creators.add())
        })
    }
}
```

### 可扩展

&#x20;从前面的例子大家已经可以看到，我们是从Duck开始继承的，当然我们还可以继续继承扩展。

```typescript
class AnotherDuck extends MyDuck{
    get reducers()...
    get quickTypes()...
    *saga()...
}
```

### 可组合

&#x20;在最终用到Redux store上时，我们需要把ducks按路径拼装，比如我们实现了一个列表交互ListDuck，现在界面上有两个列表，那么可以这样组合成一个Duck

```typescript
import { DuckMap } from 'saga-duck'

class MyDuck extends DuckMap{
    get quickDucks(){
        return {
            ...super.quickDucks,
            list1: ListDuck,
            list2: ListDuck
        }
    }
    *saga(){
        const {ducks:{list1, list2}} = this
        const list1Data = list1.selectors.list(yield select()) // 列表1的数据
    }
}
```

&#x20;注： DuckMap继承自Duck，有它的一切功能。

### 使用到React上

&#x20;最终所有业务可以组合到一个RootDuck上，然后可以通过DuckRuntime来自动执行起来，并便捷地与React组件关联起来

```typescript
import { Provider } from 'react-redux'
import { DuckRuntime } from 'saga-duck'
const rootDuck = new RootDuck()
const duckRuntime = new DuckRuntime(rootDuck)
// 也可以使用decorator快速定义 @duckRuntime.connectRoot()
const ConnectedContainer = duckRuntime.connectRoot()(Container) 

ReactDOM.render(
    <Provider store={duckRuntime.store}>
        <ConnectedContainer />
    </Provider>,
    document.getElementById('root')
)
```

&#x20;Container中可以这样访问Redux store及duck相关内容

```typescript
function Container(props){
    const { duck, store, dispatch } = props
    const { selectors, creators, ducks: { list1, list2 } } = duck
    // const xxx = selectors.xxx(store)
    // const list1Data = list1.selectors.list(store)
}
```

&#x20;**注意：建议所有与duck相关的React组件统一使用约定的 { duck, store, dispatch } 格式来传递到子组件，方便我们后续进行简单的性能优化。详情请看 进阶**

### 支持Typescript

&#x20;2.0版我们引入了typescript，在VS Code下可以做到比较方便的代码提示及纠错，但只支持到typescript 2.6.1

3.0版我们对Duck的定义方法进行了大改，由泛型+Options改为全getter方式，您无需显式地声明任何类型，一切类型都会自动识别生成。但注意只支持typescript3.0+

更多的对比请看这里
