tl; dr我想知道在redux架构中将上下文特定的多步异步回调逻辑放在哪里,如果我在下面提供的示例代码的正确轨道上。通过“多步”和“特定于上下文”,我通常表示由某些用户操作(onClicks等)启动的服务器调用,其中逻辑可能仅与给定组件相关(例如在成功时重定向到给定路由)。
有关副作用的代码的redux docs has this to say:
通常,Redux建议具有副作用的代码应该是动作创建过程的一部分。虽然该逻辑可以在UI组件内部执行,但通常将该逻辑提取到可重用的函数中是有意义的,这样可以从多个位置调用相同的逻辑 - 换句话说,就是一个动作创建器函数。
虽然这看起来不错,但我并不完全确定将调用放到我的路由组件是否“正确”,因为这些动作创建者通常看起来非常通用,并且触发路由到应用程序中的其他资源通常非常依赖上下文。
我还发现,放置这些完全不同的动物,以异步方式触发动作创建者,并在与“干净”同步动作创建者相同的文件(foo-model/actions.js
)中调度生成的动作,这有点奇怪。这是对的地方吗?在阅读Redux上的教程时,它们似乎并存。
示例代码非常简单,基本上描述了这些步骤:
背景:我希望通过将所有Meteor特定位从React组件中移出来逐步重构Meteor项目,最终将Meteor替换为正面和背面的其他内容。由于大约有50KLOC,我不能一次性完成这项工作,所以我逐步通过一条路径,希望最终得到一个标准的React + Redux + ReduxRouter包。在当前的代码路由中,数据获取和呈现在每个组件中都有些交织在一起,我在查找多步异步逻辑的位置时遇到了一些麻烦,例如下面的例子。
关于筹码的详细信息我试图解决这个问题:
MyContainerComponent中的旧Meteor代码
// triggered as onClick={(e) => this.saveEncounter(e.target.value)}
// in render()
const saveEncounter = (encounter) => {
Meteor.call('createEncounter', encounter, handleSaveResult);
}
};
const handleSaveResult = (err, encounterId) => {
if (err) {
this.setState({errorMessages: err});
} else {
// route to another page
NavigationActions.goTo('encounter', {encounterId: this.props.encounter._id || encounterId});
}
}
新的redux代码 - 移入actions.js
我正在努力保持实施直接(没有额外的deps)此时理解基础。使用redux-thunk
,redux-actions
或redux-saga
的“简化”需要稍后进行。以example code in the Redux tutorial for Async Actions
export const saveEncounter = (encounter) => {
function handleSave(err, encounterId) {
if (err) {
dispatch(createEncounterFailure(err), encounter);
} else {
dispatch(createEncounterSuccess(encounterId));
}
}
dispatch(createEncounterRequest(encounter));
Meteor.call('createEncounter', encounter, handleSave);
}
// simple sync actions creators
export const CREATE_ENCOUNTER_REQUEST = 'CREATE_ENCOUNTER_REQUEST';
function createEncounterRequest(encounter) {
return {
type: CREATE_ENCOUNTER_REQUEST,
encounter
};
}
export const CREATE_ENCOUNTER_FAILURE = 'CREATE_ENCOUNTER_FAILURE';
function createEncounterFailure(error, encounter) {
return {
type: CREATE_ENCOUNTER_FAILURE,
error,
encounter
};
}
export const CREATE_ENCOUNTER_SUCCESS = 'CREATE_ENCOUNTER_SUCCESS';
function createEncounterSuccess(encounterId) {
return {
type: CREATE_ENCOUNTER_SUCCESS,
encounterId
};
}
答案 0 :(得分:1)
我明白你的意思,你想有办法划分和分类你的行为,是吗?将执行同步代码,异步代码,记录器等的操作
就个人而言,我使用了一些命名约定。如果我必须发送一个必须获取某些数据的操作,我称之为REQUEST_DATA
。如果必须将从服务器到达的一些数据存储到ReduxStore
,我称之为STORE_DATA
。
我没有特定的模式。我还必须指出我根据功能划分我的代码库,所以我定义我的动作的模块非常小而且整洁
答案 1 :(得分:1)
正如您在评论中指出的那样,Dan Abramov在他对how to dispatch an action with a timeout的回答中讨论了处理Redux异步工作的许多想法。他还在why do we need middleware for async flow in Redux?中写了另一个很好的答案。
您可能需要阅读我Redux Side Effects的React/Redux links list类别中的其他一些文章,以便更好地了解在Redux中处理异步逻辑的方法。
一般来说,听起来你可能想要使用" sagas"或"可观察者"用于管理您的某些异步逻辑和工作流程。有异形行为的Redux中间件种类繁多 - 我在博文The Tao of Redux, Part 2 - Practice and Philosophy中总结了主要类别和最受欢迎的库。在一个名为Redux Saga in Action的帖子中,对于一个非常分离的基于传奇的Redux架构也有一些有趣的想法。
答案 2 :(得分:0)
根据我使用Redux的经验,我没有发现将异步调用放入动作创建者中的任何问题。我认为redux-thunk
或其他一些中间件非常有用,即使是简单的设置。
我唯一要补充的是,我发现你的示例代码不太可读。
就我个人而言,我喜欢the ducks pattern,但只是将动作类型,动作创建者和缩减器保存在单独的文件中可以提高清晰度。
希望这有帮助。