在 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