现在有很多关于 redux town 的最新小子 redux-saga/redux-saga 的 讨论。它使用生成器函数来监听/调度动作。
在我全神贯注之前,我想知道使用 redux-saga
的优缺点,而不是下面我使用 redux-thunk
和异步/等待的方法。
一个组件可能看起来像这样,像往常一样分派操作。
import { login } from 'redux/auth';
class LoginForm extends Component {
onClick(e) {
e.preventDefault();
const { user, pass } = this.refs;
this.props.dispatch(login(user.value, pass.value));
}
render() {
return (<div>
<input type="text" ref="user" />
<input type="password" ref="pass" />
<button onClick={::this.onClick}>Sign In</button>
</div>);
}
}
export default connect((state) => ({}))(LoginForm);
然后我的动作看起来像这样:
// auth.js
import request from 'axios';
import { loadUserData } from './user';
// define constants
// define initial state
// export default reducer
export const login = (user, pass) => async (dispatch) => {
try {
dispatch({ type: LOGIN_REQUEST });
let { data } = await request.post('/login', { user, pass });
await dispatch(loadUserData(data.uid));
dispatch({ type: LOGIN_SUCCESS, data });
} catch(error) {
dispatch({ type: LOGIN_ERROR, error });
}
}
// more actions...
// user.js
import request from 'axios';
// define constants
// define initial state
// export default reducer
export const loadUserData = (uid) => async (dispatch) => {
try {
dispatch({ type: USERDATA_REQUEST });
let { data } = await request.get(`/users/${uid}`);
dispatch({ type: USERDATA_SUCCESS, data });
} catch(error) {
dispatch({ type: USERDATA_ERROR, error });
}
}
// more actions...
原文由 hampusohlsson 发布,翻译遵循 CC BY-SA 4.0 许可协议
在 redux-saga 中,上面例子的等价物是
首先要注意的是,我们正在使用
yield call(func, ...args)
形式调用 api 函数。call
不执行效果,它只是创建一个普通对象,如{type: 'CALL', func, args}
。执行委托给 redux-saga 中间件,它负责执行函数并使用结果恢复生成器。主要优点是您可以使用简单的相等性检查在 Redux 之外测试生成器
请注意,我们通过简单地将模拟数据注入迭代器的
next
方法来模拟 api 调用结果。模拟数据比模拟函数简单得多。要注意的第二件事是调用
yield take(ACTION)
。动作创建者在每个新动作上调用 Thunk(例如LOGIN_REQUEST
)。即动作不断地 被推 送给 thunk,而 thunk 无法控制何时停止处理这些动作。在 redux-saga 中,生成器 拉 取下一个动作。也就是说,他们可以控制何时收听某些动作,何时不收听。在上面的例子中,流程指令被放置在
while(true)
循环中,所以它会监听每个传入的动作,这在某种程度上模仿了 thunk 推送行为。拉动方法允许实现复杂的控制流。例如假设我们要添加以下要求
处理 LOGOUT 用户操作
首次成功登录后,服务器会返回一个令牌,该令牌会在存储在
expires_in
字段中的某个延迟后过期。我们必须每隔expires_in
毫秒在后台刷新授权考虑到在等待 api 调用的结果(初始登录或刷新)时,用户可能会在中间注销。
你会如何用thunks来实现它?同时还为整个流程提供完整的测试覆盖率?以下是 Sagas 的外观:
在上面的示例中,我们使用
race
表达我们的并发要求。如果take(LOGOUT)
赢得比赛(即用户单击注销按钮)。比赛会自动取消authAndRefreshTokenOnExpiry
后台任务。如果authAndRefreshTokenOnExpiry
在call(authorize, {token})
调用中被阻塞,它也会被取消。取消会自动向下传播。您可以找到 上述流程的可运行演示