Redux 状态的异步初始化
Asynchronous initialization of Redux state
我真的不确定如何在 redux 应用程序中处理多部分异步初始化过程。我需要异步加载一堆数据。只有每条数据加载完成后,才算是申请"ready"。我真的不确定这应该如何在 redux 中完成。
假设一个应用程序需要完成三件事才能被考虑 ready
。这些先决条件是:
theme
已从本地文件中检索到它的定义;
userPrefs
已从本地存储中检索其设置;和
api
已确认可以访问某个远程服务器。
一方面,我似乎可以简单地添加另一个减速器(app
?)来监听这三个组件的动作。当它收到所有三个消息时,该应用程序是 "ready"。这似乎是 the advice here.
背后的想法
或者,我可能可以从消费者处获取数据。这种查找可能会发生很多次,但我想它可以被记忆化(尽管它仍然看起来很浪费)。
最后,我可以自己编写 combineReducers
以便根 reducer 有一个单独的状态树的 "top level" 分支(这看起来像是第一个选项的变体)或者写成一个reducer可以访问整个状态树。
我认为我可以编写应用程序的其余部分而无需考虑任何横切问题,但这似乎不是一种异常情况。我该如何处理这种情况?
利用 componentDidMount()
生命周期方法触发三个动作创建者,这三个动作创建者将依次启动动作以传播 reducer 的状态更改。
此组件生命周期方法将确保您的状态树已准备就绪。就那么简单。
此外,确保将动作创建者放在顶层/父组件中,可能 App.js
因为您希望在应用程序加载时 bootstrap 这些状态。
我通常将 reducer 分成不同的文件——每个文件表示一个特定的状态树。例如,我喜欢将认证相关的 reducer 与 UI 相关的 reducer 分开,等等。然后你需要像你提到的那样使用combineReducers
。
我接受了上面 James J. 的回答,因为他花时间给出了一个完全有效的答案,我认为他是一个很好的解决方案。不过,我认为我也应该包括我最终采用的解决方案。没有一个正确答案,但我不喜欢将任何业务逻辑放在视图层中的想法(感觉它不应该存在,redux 提供了其他替代方案)。
首先,这是一个无聊的顶级连接组件。唯一需要注意的是通过 operators
公开的 initializeApplication()
方法道具和 ready
状态道具(通过来自 redux 的 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 结构。在鸭子中,所有动作都通过 "operators" 公开(基本上,类型由动作包装,由动作创建者包装,由操作符包装)。
// 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
};
显然,此处调用的运算符必须 return 承诺仅在完成后才解析 运行。例如:
// 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
};
这里唯一有争议的是,其中一些鸭子式减速器模块必然 "know" 关于其他鸭子(application
知道 userPrefs
和 api
)并且直接调度其操作符,引入紧密绑定,但由于 redux 允许分层化简器树,我不认为这会破坏游戏规则,但我明白为什么有些人也不喜欢这个解决方案。
我真的不确定如何在 redux 应用程序中处理多部分异步初始化过程。我需要异步加载一堆数据。只有每条数据加载完成后,才算是申请"ready"。我真的不确定这应该如何在 redux 中完成。
假设一个应用程序需要完成三件事才能被考虑 ready
。这些先决条件是:
theme
已从本地文件中检索到它的定义;userPrefs
已从本地存储中检索其设置;和api
已确认可以访问某个远程服务器。
一方面,我似乎可以简单地添加另一个减速器(app
?)来监听这三个组件的动作。当它收到所有三个消息时,该应用程序是 "ready"。这似乎是 the advice here.
或者,我可能可以从消费者处获取数据。这种查找可能会发生很多次,但我想它可以被记忆化(尽管它仍然看起来很浪费)。
最后,我可以自己编写 combineReducers
以便根 reducer 有一个单独的状态树的 "top level" 分支(这看起来像是第一个选项的变体)或者写成一个reducer可以访问整个状态树。
我认为我可以编写应用程序的其余部分而无需考虑任何横切问题,但这似乎不是一种异常情况。我该如何处理这种情况?
利用 componentDidMount()
生命周期方法触发三个动作创建者,这三个动作创建者将依次启动动作以传播 reducer 的状态更改。
此组件生命周期方法将确保您的状态树已准备就绪。就那么简单。
此外,确保将动作创建者放在顶层/父组件中,可能 App.js
因为您希望在应用程序加载时 bootstrap 这些状态。
我通常将 reducer 分成不同的文件——每个文件表示一个特定的状态树。例如,我喜欢将认证相关的 reducer 与 UI 相关的 reducer 分开,等等。然后你需要像你提到的那样使用combineReducers
。
我接受了上面 James J. 的回答,因为他花时间给出了一个完全有效的答案,我认为他是一个很好的解决方案。不过,我认为我也应该包括我最终采用的解决方案。没有一个正确答案,但我不喜欢将任何业务逻辑放在视图层中的想法(感觉它不应该存在,redux 提供了其他替代方案)。
首先,这是一个无聊的顶级连接组件。唯一需要注意的是通过 operators
公开的 initializeApplication()
方法道具和 ready
状态道具(通过来自 redux 的 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 结构。在鸭子中,所有动作都通过 "operators" 公开(基本上,类型由动作包装,由动作创建者包装,由操作符包装)。
// 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
};
显然,此处调用的运算符必须 return 承诺仅在完成后才解析 运行。例如:
// 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
};
这里唯一有争议的是,其中一些鸭子式减速器模块必然 "know" 关于其他鸭子(application
知道 userPrefs
和 api
)并且直接调度其操作符,引入紧密绑定,但由于 redux 允许分层化简器树,我不认为这会破坏游戏规则,但我明白为什么有些人也不喜欢这个解决方案。