现在有很多人在谈论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...
为了给这个答案一些上下文:嗨,我是Redux维护者。
我们最近在Redux文档中添加了一个新的Side Effects methods页面,它将提供关于所有这些内容的许多信息,但我将尝试在这里写一些简短的内容,因为这个问题得到了大量的曝光。
在2022年,我们将监听器中间件添加到官方Redux工具包中,用于“响应式Redux逻辑”。它可以做sagas可以做的大部分事情(例外是通道),而不需要生成器语法和更好的TypeScript支持。
但这并不意味着你应该用侦听器中间件来编写所有的东西——我们建议在可能的情况下总是先使用thks,在thks不能做你想做的事情时再使用侦听器中间件。
一般来说,我们在2023年的立场是,只有当你有其他中间件无法满足的特定需求时,你才应该使用saga。(本质上:如果你需要频道。)
我们的建议是:
数据抓取
使用RTK Query作为数据获取和缓存的默认方法
如果RTKQ不完全适合,使用createAsyncThunk
只有在其他方法都没用的情况下,才会求助于手写的感谢信
不要使用saga或可观察对象来获取数据!
对动作/状态变化做出反应,异步工作流
使用RTK监听器作为默认响应存储更新和编写长时间运行的异步工作流
只有当监听器不能很好地解决你的用例时,才使用saga / observable
带有状态访问的逻辑
对于复杂的同步和适度的异步逻辑,包括对getState的访问和调度多个操作,可以使用坦克
我只是想从我的个人经验(使用sagas和thunk)中补充一些评论:
传奇故事很适合测试:
您不需要模拟用效果包装的函数
因此,测试是干净的,可读的,易于编写的
在使用saga时,动作创建者通常会返回简单的对象字面量。与thunk的承诺不同,它也更容易测试和断言。
传说更有力量。你在一个坦克动作创造者中可以做到的所有事情,你也可以在一个传奇中做到,但反之亦然(至少不容易)。例如:
等待一个/多个动作被分派(采取)
取消现有的例程(取消,taklatest, race)
多个例程可以监听相同的动作(take, takeEvery,…)
Sagas还提供了其他有用的功能,它概括了一些常见的应用程序模式:
监听外部事件源的通道(例如websockets)
Fork模型(Fork, spawn)
节气门
...
Sagas are great and powerful tool. However with the power comes responsibility. When your application grows you can get easily lost by figuring out who is waiting for the action to be dispatched, or what everything happens when some action is being dispatched. On the other hand thunk is simpler and easier to reason about. Choosing one or another depends on many aspects like type and size of the project, what types of side effect your project must handle or dev team preference. In any case just keep your application simple and predictable.
我最近加入了一个大量使用redux-saga的项目,因此也有兴趣了解更多关于saga方法的好处。
说实话,我还在找。读了这篇文章和许多喜欢它的人,“优点”是难以捉摸的。以上的答案似乎可以总结为:
可测试性(忽略实际的API调用),
很多辅助函数,
熟悉服务器端编码的开发人员。
许多其他的说法似乎过于乐观,误导或根本是错误的!我看到过许多不合理的说法,例如“坦克不能做X”。但是坦克是功能。如果一个函数不能做X,那么javascript也不能做X,所以saga也不能做X。
对我来说,缺点是:
confounding of concerns by using generator functions. Generators in JS return custom iterators. That is all. They do not have any special ability to handle async calls or to be cancellable. Any loop can have a break-out condition, any function can handle async requests, and any code can make use of a custom iterator. When people say thing like: generators have control when to listen for some action or generators are cancellable, but async calls are not then it creates confusion by implying that these qualities are inherent in - or even unique to - generator functions.
unclear use-cases: AFAIK the SAGA pattern is for handling concurrent transaction issues across services. Given that browsers are single-threaded, it is hard to see how concurrency presents a problem that Promise methods can't handle. BTW: it is also hard to see why that class of problem should ever be handled in the browser.
code traceability: By using redux middleware to turn dispatch into a kind of event-handling, Sagas dispatch actions that never reach the reducers, and so never get logged by Redux tools. While other libraries also do this, it is often unnecessarily complicated, given that browsers have event-handling built in. The advantage of the indirection is again elusive, when calling the saga directly would be more obvious.
如果这篇文章让我对传奇故事感到沮丧,那是因为我对传奇故事感到沮丧。它们似乎是一个寻找问题的伟大解决方案。国际海事组织。