# 介绍&入门

## 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+

更多的对比请看这里


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://cyrilluce.gitbook.io/saga-duck/master.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
