在 onClick 处理程序中使用 context.store.getState() 是否被视为反模式?
Is using context.store.getState() in an onClick handler considered as anti-pattern?
我有以下简化的仪表板组件。仪表板对象是通过道具注入的。 handleDeleteDashboard 操作检查仪表板是否不是最后一个可用的仪表板。如果是,则不允许删除它。对于此检查,我需要从 mapStateToProps 中的商店获取的 nrOfDashboards。所以我将组件连接到 redux 存储。
class Dashboard extends Component {
constructor(props) {
super(props);
this.handleDeleteDashboard = this.handleDeleteDashboard.bind(this);
}
handleDeleteDashboard() {
const { dashboardDeleteAction, dashboard, nrOfDashboards } = this.props;
if (nrOfDashboards < 2) {
// NOT Allowed to delete
} else {
dashboardDeleteAction(dashboard.id);
}
}
render() {
const { dashboard } = this.props;
return (
<Content>
<h1>{dashboard.name}</h1>
<Button onButtonClick={this.handleDeleteDashboard}>Delete</Button>
</Content>
);
}
}
Dashboard.propTypes = {
dashboard: customPropTypes.dashboard.isRequired,
nrOfDashboards: PropTypes.number.isRequired
};
function mapStateToProps(state) {
return {
nrOfDashboards: selectNrOfDashboards(state)
}
}
export default connect(mapStateToProps, { dashboardDeleteAction: dashboardActionCreators.dashboardDelete })(Dashboard);
但现在组件已订阅到商店并在 nrOfDashboards 更改时更新(我知道我可以在此处执行 shouldComponentUpdate 以防止重新呈现,但这不是重点)。所以我基本上订阅了 nrOfDashboards 上的更改,尽管我只在主动单击删除按钮时才需要此信息。
所以我想出了一个替代解决方案,我将组件与商店断开连接,并通过 handleDeleteDashboard 方法中的上下文访问商店。
class Dashboard extends Component {
constructor(props) {
...
}
handleDeleteDashboard() {
const { dashboardDeleteAction, dashboard } = this.props;
const store = this.context;
if (selectNrOfDashboards(store.getState()) < 2) {
// NOT Allowed to delete
} else {
dashboardDeleteAction(dashboard.id);
}
}
render() {
...
}
}
Dashboard.propTypes = {
dashboard: customPropTypes.dashboard.isRequired,
};
Dashboard.contextTypes = {
store: PropTypes.object
};
export default connect(null, { dashboardDeleteAction: dashboardActionCreators.dashboardDelete })(Dashboard);
这对我来说效果很好,每当我主动单击按钮时,我都会确保从商店中获取最新状态。无论如何,我之前没有在其他地方看到过这种技术,并且还在某处读到访问商店不应该在 mapStateToProps 之外完成。但我的问题是,如果按需直接访问商店是一种反模式,我是否应该更好地遵循代码示例一,我在哪里将组件连接到商店?
是的。直接访问商店被认为是一种反模式。惯用的 Redux 代码使用基本的依赖注入 - connect()
及其 mapState()
和 mapDispatch()
参数为您提供组件所需的数据和对 dispatch
的引用,以及像 Redux-Thunk 这样的中间件使您可以在动作创建器中访问 getState()
和 dispatch()
。
理想情况下,您的组件将简单地分派一个动作创建者,并让动作创建者逻辑担心是否真的分派一个真正的动作。所以,在你的情况下,它可能看起来像:
// action creator
export function deleteDashboard(dashboardID) {
return (dispatch, getState) => {
const state = getState();
const numberOfDashboards = selectNumberOfDashboards(state);
if(numberOfDashboards >= 2) {
dispatch({
type : "DELETE_DASHBOARD",
payload : {
dashboardID
}
});
}
}
}
// component
handleDeleteDashboard() {
const {dashboard} = this.props;
this.props.dispatch(deleteDashboard(dashboard.id));
}
请参阅有关此主题的 Redux 常见问题解答:http://redux.js.org/docs/FAQ.html#store-setup-multiple-stores
我有以下简化的仪表板组件。仪表板对象是通过道具注入的。 handleDeleteDashboard 操作检查仪表板是否不是最后一个可用的仪表板。如果是,则不允许删除它。对于此检查,我需要从 mapStateToProps 中的商店获取的 nrOfDashboards。所以我将组件连接到 redux 存储。
class Dashboard extends Component {
constructor(props) {
super(props);
this.handleDeleteDashboard = this.handleDeleteDashboard.bind(this);
}
handleDeleteDashboard() {
const { dashboardDeleteAction, dashboard, nrOfDashboards } = this.props;
if (nrOfDashboards < 2) {
// NOT Allowed to delete
} else {
dashboardDeleteAction(dashboard.id);
}
}
render() {
const { dashboard } = this.props;
return (
<Content>
<h1>{dashboard.name}</h1>
<Button onButtonClick={this.handleDeleteDashboard}>Delete</Button>
</Content>
);
}
}
Dashboard.propTypes = {
dashboard: customPropTypes.dashboard.isRequired,
nrOfDashboards: PropTypes.number.isRequired
};
function mapStateToProps(state) {
return {
nrOfDashboards: selectNrOfDashboards(state)
}
}
export default connect(mapStateToProps, { dashboardDeleteAction: dashboardActionCreators.dashboardDelete })(Dashboard);
但现在组件已订阅到商店并在 nrOfDashboards 更改时更新(我知道我可以在此处执行 shouldComponentUpdate 以防止重新呈现,但这不是重点)。所以我基本上订阅了 nrOfDashboards 上的更改,尽管我只在主动单击删除按钮时才需要此信息。
所以我想出了一个替代解决方案,我将组件与商店断开连接,并通过 handleDeleteDashboard 方法中的上下文访问商店。
class Dashboard extends Component {
constructor(props) {
...
}
handleDeleteDashboard() {
const { dashboardDeleteAction, dashboard } = this.props;
const store = this.context;
if (selectNrOfDashboards(store.getState()) < 2) {
// NOT Allowed to delete
} else {
dashboardDeleteAction(dashboard.id);
}
}
render() {
...
}
}
Dashboard.propTypes = {
dashboard: customPropTypes.dashboard.isRequired,
};
Dashboard.contextTypes = {
store: PropTypes.object
};
export default connect(null, { dashboardDeleteAction: dashboardActionCreators.dashboardDelete })(Dashboard);
这对我来说效果很好,每当我主动单击按钮时,我都会确保从商店中获取最新状态。无论如何,我之前没有在其他地方看到过这种技术,并且还在某处读到访问商店不应该在 mapStateToProps 之外完成。但我的问题是,如果按需直接访问商店是一种反模式,我是否应该更好地遵循代码示例一,我在哪里将组件连接到商店?
是的。直接访问商店被认为是一种反模式。惯用的 Redux 代码使用基本的依赖注入 - connect()
及其 mapState()
和 mapDispatch()
参数为您提供组件所需的数据和对 dispatch
的引用,以及像 Redux-Thunk 这样的中间件使您可以在动作创建器中访问 getState()
和 dispatch()
。
理想情况下,您的组件将简单地分派一个动作创建者,并让动作创建者逻辑担心是否真的分派一个真正的动作。所以,在你的情况下,它可能看起来像:
// action creator
export function deleteDashboard(dashboardID) {
return (dispatch, getState) => {
const state = getState();
const numberOfDashboards = selectNumberOfDashboards(state);
if(numberOfDashboards >= 2) {
dispatch({
type : "DELETE_DASHBOARD",
payload : {
dashboardID
}
});
}
}
}
// component
handleDeleteDashboard() {
const {dashboard} = this.props;
this.props.dispatch(deleteDashboard(dashboard.id));
}
请参阅有关此主题的 Redux 常见问题解答:http://redux.js.org/docs/FAQ.html#store-setup-multiple-stores