初学者教程
本教程的目标
本教程试图以一种(希望)易于理解的方式介绍redux-saga。
在我们的入门教程中,我们将使用Redux仓库中的简单Counter演示。这个应用程序非常基础,但很适合阐述redux-saga的基本概念,而不会迷失在过多的细节中。
初始设置
在我们开始之前,克隆教程仓库。
本教程的最终代码位于
sagas
分支。
然后在命令行中运行:
$ cd redux-saga-beginner-tutorial
$ npm install
要启动应用程序,运行:
$ npm start
编译完成后,用浏览器打开http://localhost:9966。
我们从最基本的用例开始:2个按钮Increment
和Decrement
用于计数器。稍后,我们将引入异步调用。
如果一切顺利,你应该看到2个按钮Increment
和Decrement
,以及下面显示Counter: 0
的消息。
如果你在运行应用程序时遇到问题,欢迎在教程仓库创建问题。
Hello Sagas!
我们将创建我们的第一个Saga。按照传统,我们将为Sagas编写我们的'Hello, world'版本。
创建一个sagas.js
文件,然后添加以下代码片段:
export function* helloSaga() {
console.log('Hello Sagas!')
}
所以没有什么可怕的,只是一个普通的函数(除了*
)。它所做的只是在控制台打印一个问候消息。
为了运行我们的Saga,我们需要:
- 创建一个带有要运行的Sagas列表的Saga中间件(到目前为止,我们只有一个
helloSaga
) - 将Saga中间件连接到Redux store
我们将对main.js
进行更改:
// ...
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
// ...
import { helloSaga } from './sagas'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(helloSaga)
const action = type => store.dispatch({type})
// 其余部分不变
首先,我们从./sagas
模块导入我们的Saga。然后我们使用redux-saga
库导出的工厂函数createSagaMiddleware
创建一个中间件。
在运行我们的helloSaga
之前,我们必须使用applyMiddleware
将我们的中间件连接到Store。然后我们可以使用sagaMiddleware.run(helloSaga)
来启动我们的Saga。
到目前为止,我们的Saga没有做什么特别的事情。它只是记录一条消息然后退出。
进行异步调用
现在让我们添加一些更接近原始 Counter 示例的内容。为了说明异步调用,我们将添加另一个按钮,在点击后一秒钟增加计数器。
首先,我们将为 UI 组件提供一个额外的按钮和一个回调 onIncrementAsync
。
我们将对 Counter.js
进行更改:
const Counter = ({ value, onIncrement, onDecrement, onIncrementAsync }) =>
<div>
<button onClick={onIncrementAsync}>
一秒后增加
</button>
{' '}
<button onClick={onIncrement}>
增加
</button>
{' '}
<button onClick={onDecrement}>
减少
</button>
<hr />
<div>
点击了: {value} 次
</div>
</div>
接下来,我们应该将组件的 onIncrementAsync
连接到 Store 操作。
我们将如下修改 main.js
模块
function render() {
ReactDOM.render(
<Counter
value={store.getState()}
onIncrement={() => action('INCREMENT')}
onDecrement={() => action('DECREMENT')}
onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
document.getElementById('root')
)
}
注意,与 redux-thunk 不同,我们的组件分派一个普通对象操作。
现在我们将引入另一个 Saga 来执行异步调用。我们的用例如下:
对于每一个
INCREMENT_ASYNC
操作,我们希望启动一个任务,该任务将执行以下操作
- 等待 1 秒,然后增加计数器
将以下代码添加 到 sagas.js
模块:
import { put, takeEvery } from 'redux-saga/effects'
const delay = (ms) => new Promise(res => setTimeout(res, ms))
// ...
// 我们的 worker Saga:将执行异步增加任务
export function* incrementAsync() {
yield delay(1000)
yield put({ type: 'INCREMENT' })
}
// 我们的 watcher Saga:在每个 INCREMENT_ASYNC 上产生一个新的 incrementAsync 任务
export function* watchIncrementAsync() {
yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}