我花了几周的时间研究这个问题并且没有达成共识。我有一个带有事件处理程序的组件。他们需要使用与组件无关的状态数据来执行业务逻辑,并根据该组件和组件数据分派不同的操作。最好的方法是什么?我列出了一些非常简单的例子,这些例子并不完整,但显示了我所谈论的内容的主旨。
我可以轻松传递组件所需的所有内容,包括与其无关的状态数据。容器不需要扩展React.Container。
MyContainer.js
const mapStateToProps = (state) => {
return ({
foo: state.my.foo,
bar: state.other.bar
})
};
const mapDispatchToProps = (dispatch) => ({
doSomething: (value) => {
dispatch(someAction(value));
}
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(MyComponent);
MyComponent.js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.onClickHandler = this.onClickHandler.bind(this);
}
onClickHandler() {
if ( complicated logic with this.props.bar ) {
this.props.doSomething(this.props.foo);
}
}
render() {
return (
<button onClick={this.onClickHandler} />
)
}
}
优点: 非常简单。组件需要的所有东西都会传递给它。
CONS: 该组件正在做业务逻辑,这看起来并不正确。但我无法在容器中轻松执行此逻辑,因为它不容易访问不相关的状态数据。
ALTERNATIVE: 我可以将所有这些道具传递到组件的doSomething()中,让容器的doSomething()执行逻辑。但这意味着容器从未使用过一些道具,它们只是通过了。但这会将业务逻辑重新放回容器中。
我可以用适当的React.Component类定义容器,通过props传递它需要的状态数据,定义一个函数来使用这个状态和本地状态数据,然后渲染它。
class MyContainer extends React.Component {
constructor(props) {
super(props);
this.onClickHandler = this.onClickHandler.bind(this);
}
onClickHandler(foo) {
if ( this.props.bar ) {
this.props.dispatch(foo)
}
}
render() {
return (
<MyComponent onClickHandler={this.onClickHandler} {...this.props} />
)
}
}
优点: 业务逻辑可以在容器中发生。
CONS: 很多仪式。容器必须传递其业务逻辑功能所需的任何状态数据。
我还没试过这个。但是connect()的第三个参数公开了mapState和mapDispatch的结果,因此您可以生成依赖于mapState数据的函数。
我甚至没有试过这个,因为Dan Abramov暗示它并不好,并且可能会对性能产生影响。
只需传递组件所需的任何数据。调度一个使用getState()来查看业务逻辑所需的状态数据的函数。然后它可以发送任何最终行动。
MyReducer.js
export function actionDecider(foo) {
return (dispatch, getState) => {
const state = getState();
if ( complicated logic with state.other.bar ) {
dispatch(someAction(foo));
}
};
}
MyContainer.js
const mapStateToProps = (state) => {
return ({
foo: state.my.foo
})
};
const mapDispatchToProps = (dispatch) => ({
doSomething: (value) => {
dispatch(actionDecider(value));
}
})
export default connect(
mapStateToProps,
(dispatch) => ({
...mapDispatchToProps(dispatch)
})
)(MyComponent);
MyComponent.js
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.onClickHandler = this.onClickHandler.bind(this);
}
onClickHandler() {
this.props.doSomething(this.props.foo);
}
render() {
return (
<button onClick={this.onClickHandler} />
)
}
}
优点: 更少的代码。很容易掌握。无需将状态数据填充到奇怪的区域,只需在需要时访问它。
CONS: 关于这是否是反模式的意见分歧。甚至丹·阿布拉莫夫也表达了一些反对意见,尽管有人说这是过分夸大其词。巨大的州树存在性能问题吗?
就个人而言,我喜欢在方法4中使用thunk。虽然我也可以看到方法1上的替代方法。有正确的方法吗?有错误的方法吗?