我真的不确定如何处理redux应用程序中的多部分异步初始化过程。我需要异步加载一堆数据。只有在每个数据完成加载后,才能将应用程序视为“就绪”。我真的不确定应该如何在redux中完成。
假设应用程序在被视为ready
之前需要完成三件事。这些先决条件是:
theme
已从本地文件中检索其定义; userPrefs
已从本地存储中检索其设置;和api
已确认可以访问某个远程服务器。一方面,似乎我可以简单地添加另一个缩减器(app
?)来监听这三个组件的动作。当它从三个人那里听到时,该应用程序已“准备就绪”。这似乎是the advice here背后的想法。
或者,我可以在消费者处获得数据。这种查找可能会发生很多,但我想它可以被记忆(虽然它看起来仍然很浪费)。
最后,我可以看到自己编写自己的combineReducers
,以便根减少器具有状态树的单独“顶级”分支(这看起来像是第一个选项的变体)或者写它这样一个reducer就可以访问整个状态树了。
我认为我可以在没有任何横切关注的情况下编写应用程序的其余部分,但这似乎不是一个不寻常的情况。我该如何处理这种情况?
答案 0 :(得分:1)
利用Event Year Variable1 First_occurrence
event1 1994 False False
event1 1995 True True
event1 1996 True False
event1 1997 True False
event2 2000 False False
event2 2001 False False
event2 2002 True True
生命周期方法触发三个动作创建者,这三个动作创建者将依次启动动作以通过reducer传播状态更改。
此组件生命周期方法将确保您的状态树已准备就绪。就那么简单。
另外,请确保将动作创建者放在顶级/父级组件中,可能是componentDidMount()
,因为您希望在应用程序加载时引导这些状态。
我通常将reducer分成不同的文件 - 每个文件表示一个特定的状态树。例如,我喜欢将与身份验证相关的Reducer与UI相关的reducer分开,依此类推。然后你需要使用你提到的App.js
。
答案 1 :(得分:1)
我接受了James J.的回答,因为他花时间给出了一个完全有效的答案,我认为他是一个很好的解决方案。不过,我认为我应该包括我最终的解决方案。没有一个正确的答案,但我不喜欢在视图层中放置任何业务逻辑的想法(它只是觉得它不应该存在而且redux提供其他替代方案)。
首先,这是一个无聊的顶级连接组件。唯一需要注意的是通过initializeApplication()
公开的operators
方法道具,以及来自redux的ready
州道具(可通过state.application
获得。
// components/Application.js
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { operators, selectors } from './store'; // eslint-disable-line no-unused-vars
class Application extends React.Component {
componentDidMount() {
this.props.initializeApplication();
}
render() {
return <div>Application ready? { this.props.application.ready }</div>;
}
}
const mapStateToProps = state => ({
application: state.application,
});
const mapDispatchToProps = ({
initializeApplication: operators.initializeApplication,
});
export default connect(mapStateToProps, mapDispatchToProps)(Application);
这是应用程序的redux部分。它使用a variation of the ducks结构。在鸭子中,所有动作都通过&#34;运营商&#34; (基本上,类型由动作包装,动作由动作创建者包裹,由操作员包装)。
// store/Application/index.js
import { createReducer } from '../../Utils';
import { operators as UserPrefs } from '../UserPrefs';
import { operators as Api } from '../Api';
// TYPES:
const types = {
INITIALIZED: 'Application/initialized'
};
// ACTION CREATORS:
const actions = {
initialized: () => ({ type: INITIALIZED })
};
// OPERATORS:
const initializeApplication = () => dispatch =>
Promise.all([
dispatch(UserPrefs.initializeUserPrefs()),
dispatch(Api.initializeApi())
])
.then(() => dispatch(actions.initialized()));
const operators = {
initializeApplication
};
// REDUCER:
const initialShape = {
ready: false
};
const reducer = createReducer(initialShape)({
[INITIALIZED]: (state) => ({...state, ready: true })
});
// EXPORTS
export default reducer;
export {
operators,
types
};
显然,此处调用的运算符必须返回仅在运行完成后才能解析的promise。例如:
// store/UserPrefs/index.js
// ** NOT INCLUDING REDUCERS, TYPES, ETC **
const initializeUserPrefs = () => dispatch =>
Promise.all([
loadDefaultPrefs(),
loadCurrentPrefs()
])
.then(
([
defaultPrefs,
currentPrefs
]) => dispatch(actions.initialized(defaultPrefs, currentPrefs))
);
const operators = {
initializeInventory
};
export {
operators
};
这里唯一有争议的事情是这些鸭式减速机模块中的一些必然会知道&#34;关于其他鸭子(application
知道userPrefs
和api
)并直接调度其运算符,引入紧密绑定,但由于redux允许分层减少树,我不认为这是一个游戏破坏者,但我可以看到为什么有些人不喜欢这个解决方案。