现在有很多关于redux镇最新孩子的讨论,redux-saga/redux-saga . 它使用生成器函数来监听/分派操作 .
在我绕过它之前,我想知道使用 redux-saga
的优缺点,而不是下面的方法,我正在使用 redux-thunk
与async / await .
组件可能看起来像这样,像往常一样调度动作 .
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...
7 回答
更简单的方法是使用redux-auto .
来自文件
不需要其他Redux异步中间件 . 例如thunk,promise-middleware,saga
轻松允许您将承诺传递给redux and have it managed for you
允许您将外部服务调用与其转换位置共同定位
命名文件"init.js"将在应用启动时调用一次 . 这适用于在开始时从服务器加载数据
这个想法是每个action in a specific file . 使用"pending","fulfilled"和"rejected"的reducer函数共同定位文件中的服务器调用 . 这使得处理承诺变得非常容易 .
它还会自动将helper object(called "async")附加到您所在州的原型,允许您在UI中跟踪请求的转换 .
除了库作者的相当彻底的答案之外,我将在 生产环境 系统中添加使用saga的经验 .
Pro(使用传奇):
可测试性 . 测试sagas非常容易,因为call()返回一个纯对象 . 测试thunk通常需要在测试中包含mockStore .
redux-saga附带了许多有关任务的有用辅助函数 . 在我看来,saga的概念是为你的应用程序创建某种后台工作者/线程,它在react redux体系结构中扮演一个缺失的部分(actionCreators和reducers必须是纯函数 . )这导致了下一点 .
Sagas提供独立的处理所有副作用的地方 . 根据我的经验,修改和管理通常比thunk动作更容易 .
缺点:
生成器语法 .
需要学习很多概念 .
API稳定性 . 似乎redux-saga仍在添加功能(例如 Channels ?),社区不是那么大 . 如果库有一天会进行非向后兼容的更新,则会引起关注 .
我只是想从我的个人经历中添加一些评论(同时使用sagas和thunk):
萨加斯非常适合测试:
您不需要模拟包含效果的函数
因此,测试干净,易读且易于编写
使用传奇时,动作创建者大多返回普通对象文字 . 与thunk的承诺不同,测试和断言也更容易 .
Sagas更强大 . 所有你可以在一个thunk的动作创建者中做的事情你也可以在一个传奇中做,但反之亦然(或者至少不容易) . 例如:
等待调度操作/操作(
take
)取消现有例程(
cancel
,takeLatest
,race
)多个例程可以侦听相同的操作(
take
,takeEvery
,...)Sagas还提供其他有用的功能,它们概括了一些常见的应用程序模式:
channels
收听外部事件来源(例如websockets)前叉模型(
fork
,spawn
)油门
......
Sagas是伟大而强大的工具 . 然而,权力来自责任 . 当您的应用程序增长时,您可以通过确定谁正在等待调度操作,或者在调度某些操作时发生的一切情况轻易丢失 . 另一方面,thunk更简单,更容易推理 . 选择一个或另一个取决于许多方面,如项目的类型和大小,项目必须处理的副作用类型或开发团队偏好 . 在任何情况下,只需保持您的应用程序简单和可预测 .
根据我的经验回顾了几个不同的大规模React / Redux项目,Sagas为开发人员提供了一种更加结构化的编写代码的方法,这种代码更容易测试,更难以出错 .
是的,从一开始就有点奇怪,但大多数开发者在一天内对它有了足够的了解 . 我总是告诉别人不要担心
yield
开始做什么,一旦你写了几个测试,它就会来找你 .我已经看到了几个项目,其中thunk被视为来自MVC模式的控制器,这很快就变成了一个不可控制的混乱 .
我的建议是在你需要的地方使用Sagas触发B类与单个事件有关的东西 . 对于任何可能涉及多个操作的内容,我发现编写客户中间件并使用FSA操作的元属性来触发它更为简单 .
一个快速说明 . 发电机是可取消,异步/等待 - 不是 . 因此,对于问题的一个例子,它并没有真正理解选择什么 . 但是对于更复杂的流程,有时没有比使用生成器更好的解决方案 .
因此,另一个想法可能是使用带有redux-thunk的发电机,但对我来说,似乎试图发明一种带方形轮的自行车 .
当然,发电机更容易测试 .
在redux-saga中,相当于上面的例子
首先要注意的是我们使用
yield call(func, ...args)
形式调用api函数 .call
不执行效果,它只是创建一个像{type: 'CALL', func, args}
这样的普通对象 . 执行被委托给redux-saga中间件,该中间件负责执行该函数并使用其结果恢复生成器 .主要优点是您可以使用简单的相等性检查在Redux之外测试生成器
注意我们只是通过将模拟数据注入迭代器的
next
方法来模拟api调用结果 . 模拟数据比模拟函数更简单 .要注意的第二件事是调用
yield take(ACTION)
. 动作创建者会在每个新动作上调用Thunk(例如LOGIN_REQUEST
) . 即行动不断被推到thunk,并且thunks无法控制何时停止处理这些行动 .在redux-saga中,发电机会采取下一步行动 . 即他们有权控制何时采取某些行动,何时不采取行动 . 在上面的示例中,流指令被放置在
while(true)
循环内,因此它将侦听每个传入的操作,这有点模仿了thunk推送行为 .拉方法允许实现复杂的控制流程 . 例如,假设我们要添加以下要求
处理LOGOUT用户操作
在第一次成功登录时,服务器返回一个令牌,该令牌在
expires_in
字段中存储一些延迟 . 我们必须在每个expires_in
毫秒的后台刷新授权考虑到在等待api调用的结果(初始登录或刷新)时,用户可以在中间注销 .
你如何用thunk实现它;同时还为整个流程提供全面的测试覆盖?以下是Sagas的外观:
在上面的例子中,我们使用
race
来表达我们的并发性要求 . 如果take(LOGOUT)
赢得比赛(即用户点击了退出按钮) . 比赛将自动取消authAndRefreshTokenOnExpiry
后台任务 . 如果authAndRefreshTokenOnExpiry
在call(authorize, {token})
呼叫中间被阻止,它也将被取消 . 取消自动向下传播 .你可以找到runnable demo of the above flow
这是一个结合了
redux-saga
和redux-thunk
的最佳部分(专业)的项目:你可以通过dispatching
获得承诺来处理传奇的所有副作用:相应的动作:https://github.com/diegohaz/redux-saga-thunk