现在有很多关于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);
Run Code Online (Sandbox Code Playgroud)
然后我的行为看起来像这样:
// auth.js
import request from 'axios';
import { loadUserData } from './user';
// define constants
// define initial state
// export default reducer …
Run Code Online (Sandbox Code Playgroud) 我使用过Redux-Saga.用它编写的代码到目前为止很容易理解,除了JS生成器功能不时弄乱我的脑袋.根据我的理解,Redux-Observable可以实现处理副作用但不使用生成器功能的类似工作.
然而,来自Redux-Observable的文档并没有提供很多关于它为什么优于Redux-Saga的观点.我想知道是否使用生成器函数是使用Redux-Observable的唯一好处.使用Redux-Observable而不是Redux-Saga会有什么缺点,不足或妥协?提前致谢.
javascript reactive-programming redux redux-saga redux-observable
如何在saga函数中访问redux状态?
我有一个由以下部分组成的应用程序:
import { select } from 'redux-saga';
...
let data = yield select(stateSelectorFunction);
Run Code Online (Sandbox Code Playgroud)
每个部分都是一个不同的组件,它有自己的减速器,传奇,动作和状态.
工具栏有一个保存按钮,可以调度"SAVE_PROJECT"动作类型.问题是我不希望工具栏知道项目(它有更多的职责,我只是希望它发送类型的动作).
我有一个项目相关的传奇,它听取了"SAVE_PROJECT":
import { select } from 'redux-saga';
...
let data = yield select(stateSelectorFunction);
Run Code Online (Sandbox Code Playgroud)
我无法从传奇中的redux状态获得该项目.
我不认为我可以在当前项目reducer中听取"SAVE_PROJECT"事件,然后在reducer内部获取项目并向项目发送不同的操作.
我真的不希望我的工具栏知道整个状态树,并为每个动作发送与动作相关的任何内容.
我怎样才能将状态传递给传奇?还是只有国家的相关部分?
我有一个商品列表中的商品.当我的应用程序第一次加载时,我需要反序列化项目,就像在基于项目创建一些内存中对象一样.这些项目存储在我的redux商店中并由一个itemsReducer
.
我试图使用redux-saga来处理反序列化,作为副作用.在第一页加载时,我发送一个动作:
dispatch( deserializeItems() );
Run Code Online (Sandbox Code Playgroud)
我的传奇很简单:
function* deserialize( action ) {
// How to getState here??
yield put({ type: 'DESERISLIZE_COMPLETE' });
}
function* mySaga() {
yield* takeEvery( 'DESERIALIZE', deserialize );
}
Run Code Online (Sandbox Code Playgroud)
在我的deserialize saga中,我想要处理创建内存版本的项目的副作用,我需要从商店中读取现有数据.我不知道该怎么做,或者如果这是一种模式,我甚至应该尝试使用redux-saga.
我的问题是我应该使用哪个?Redux Toolkit 可以替代 Redux 核心吗?
我想按照这个代码在redux-saga
export const getUser = (state, login) => state.entities.users[login]
export const getRepo = (state, fullName) => state.entities.repos[fullName]
Run Code Online (Sandbox Code Playgroud)
然后将其像传奇使用此:
import { getUser } from '../reducers/selectors'
// load user unless it is cached
function* loadUser(login, requiredFields) {
const user = yield select(getUser, login)
if (!user || requiredFields.some(key => !user.hasOwnProperty(key))) {
yield call(fetchUser, login)
}
}
Run Code Online (Sandbox Code Playgroud)
这种getUser
减速器(甚至是减速器)与我通常期望的减速器看起来非常不同.
任何人都可以解释选择器是什么,getUser
减速器是如何以及它如何适应redux-saga?
redux-saga项目已经存在很长时间了,但是这个库仍然有很多令人困惑的事情.其中一个是:如何开始你的rootSaga.例如,在初学者教程中, rootSaga是通过一系列传奇来启动的.像这样
export default function* rootSaga() {
yield [
helloSaga(),
watchIncrementAsync()
]
}
Run Code Online (Sandbox Code Playgroud)
然而,在使用传奇助手部分,rootSaga由两个分叉的传奇组成.像这样:
export default function* rootSaga() {
yield fork(watchFetchUsers)
yield fork(watchCreateUser)
}
Run Code Online (Sandbox Code Playgroud)
在redux-saga repo中的异步示例中使用了与启动rootSaga相同的方法.但是,如果你查看真实世界和购物卡的例子,你会看到rootSagas那里有一系列分叉的传奇.像这样:
export default function* root() {
yield [
fork(getAllProducts),
fork(watchGetProducts),
fork(watchCheckout)
]
}
Run Code Online (Sandbox Code Playgroud)
另外,如果您在redux-saga问题中阅读了一些讨论,您会看到有些人建议使用spawn而不是fork来为rootSaga保护应用程序完全崩溃,如果您的某个分叉传奇由于某些未处理的异常而被取消.
那么,哪种方式是启动rootSaga的最正确的方法?现有的有什么区别?
下面两种方法之间有什么区别?
export function* watchLoginUser() {
yield takeEvery(USER_LOGIN, loginUser)
}
export function* watchLogoutUser() {
yield takeEvery(USER_LOGOUT, logoutUser)
}
export function* watchGetParties() {
yield takeEvery(PARTIES_GET, getParties)
}
export default function* root() {
yield [
fork(watchLoginUser),
fork(watchLogoutUser),
fork(watchGetParties)
]
}
Run Code Online (Sandbox Code Playgroud)
export default function* root() {
yield [
takeEvery(USER_LOGIN, loginUser),
takeEvery(USER_LOGOUT, logoutUser),
takeEvery(PARTIES_GET, getParties)
]
}
Run Code Online (Sandbox Code Playgroud)
我什么时候需要使用fork而不需要?
我在整个代码库中使用async/await.因此,我的api调用由异步函数定义
async function apiFetchFoo {
return await apiCall(...);
}
Run Code Online (Sandbox Code Playgroud)
我想从我的传奇代码中调用这个函数.好像我不能这样做:
// Doesn't work
function* fetchFoo(action) {
const results = await apiFetchFoo();
yield put({type: "FOOS_FETCHED_SUCCESSFULLY", foos: results});
}
Run Code Online (Sandbox Code Playgroud)
但是,这确实有效,并且与redux saga文档相匹配:
// Does work
function* fetchFoo(action) {
const results = yield call(apiFetchFoo);
yield put({type: "FOOS_FETCHED_SUCCESSFULLY", foos: results});
}
Run Code Online (Sandbox Code Playgroud)
这是使用Redux Saga和async/await的正确方法吗?在saga代码中使用这个生成器语法,在其他地方使用async/await模式是标准的吗?
目标:在加载反应路由器路由时,调度Redux操作,请求异步Saga工作程序获取该路由的基础无状态组件的数据.
问题:无状态组件仅仅是函数,没有生命周期方法,例如componentDidMount,所以我不能(?)从函数内部调度Redux操作.
我的问题部分与将有状态React组件转换为无状态功能组件有关:如何实现"componentDidMount"类功能?,但我的目标是仅发送一个Redux动作,要求将数据异步填充到商店(我使用Saga,但我认为这与问题无关,因为我的目标是仅发送一个普通的Redux动作),之后由于数据支柱的改变,无状态组件将重新渲染.
我正在考虑两种方法:使用react-router的一些功能,或Redux的connect方法.是否有一种所谓的"反应方式"来实现我的目标?
编辑:到目前为止我唯一提出的解决方案是在mapDispatchToProps中调度动作,这样:
const mapStateToProps = (state, ownProps) => ({
data: state.myReducer.data // data rendered by the stateless component
});
const mapDispatchToProps = (dispatch) => {
// catched by a Saga watcher, and further delivered to a Saga worker that asynchronically fetches data to the store
dispatch({ type: myActionTypes.DATA_GET_REQUEST });
return {};
};
export default connect(mapStateToProps, mapDispatchToProps)(MyStatelessComponent);
Run Code Online (Sandbox Code Playgroud)
然而,这似乎有点脏,而不是正确的方式.