React-Redux同功能组件复用:不同state属性映射如何实现DRY?
遵循DRY原则复用React-Redux组件的方案
你这个需求太常见啦!核心思路就是把UI/业务逻辑组件和Redux连接的容器组件拆分开,让通用的逻辑只写一次,不同的state映射交给专门的容器来处理。下面给你一步步演示怎么改:
第一步:提取通用的基础组件
把原来的MyComponent改成不依赖Redux的纯组件,所有需要的数据和方法都通过props传入,不要直接绑定Redux的state或action。比如:
import React, { Component, PropTypes } from 'react'; // 这是通用的基础组件,只负责渲染和业务逻辑 class BaseComponent extends Component { constructor(props) { super(props); } componentWillReceiveProps(nextProps) { // 这里的逻辑复用原来的,只是用props里的通用字段 if (nextProps.targetData !== this.props.targetData) { // 处理属性变化的逻辑 } } render() { // 渲染逻辑也用props里的通用字段 return ( <div> <h1>{this.props.targetData.title}</h1> <button onClick={this.props.handleAction}>触发操作</button> </div> ); } } // 定义props的类型,确保通用性 BaseComponent.propTypes = { targetData: PropTypes.object.isRequired, handleAction: PropTypes.func.isRequired }; export default BaseComponent;
第二步:创建两个不同的容器组件
分别针对不同的state属性,写自己的mapStateToProps(和mapDispatchToProps),然后用connect把基础组件和Redux store连接起来:
第一个容器组件(映射state.a)
import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { yourActionCreator } from '../actions'; import BaseComponent from './BaseComponent'; const mapStateToProps = (state) => { return { // 把state里的a属性映射到基础组件的targetData targetData: state.a }; }; const mapDispatchToProps = (dispatch) => { return { // 绑定对应的action,可根据需求调整 handleAction: bindActionCreators(yourActionCreator, dispatch) }; }; // 生成第一个容器组件 export const ComponentA = connect(mapStateToProps, mapDispatchToProps)(BaseComponent);
第二个容器组件(映射state.b)
import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { yourActionCreator } from '../actions'; import BaseComponent from './BaseComponent'; const mapStateToProps = (state) => { return { // 把state里的b属性映射到基础组件的targetData targetData: state.b }; }; // 如果action和第一个一样,直接复用;不一样就换对应的action creator const mapDispatchToProps = (dispatch) => { return { handleAction: bindActionCreators(yourActionCreator, dispatch) }; }; // 生成第二个容器组件 export const ComponentB = connect(mapStateToProps, mapDispatchToProps)(BaseComponent);
第三步:使用组件
现在你在其他地方就可以直接用ComponentA和ComponentB了,它们功能完全一样,但分别绑定了state的不同属性:
import { ComponentA, ComponentB } from './components'; const App = () => { return ( <div> <ComponentA /> <ComponentB /> </div> ); };
额外优化:封装高阶函数减少重复
如果两个容器的映射逻辑只是state的属性名不同,你甚至可以写一个高阶函数来生成容器组件,进一步简化代码:
import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { yourActionCreator } from '../actions'; import BaseComponent from './BaseComponent'; // 封装生成容器的函数,传入state的属性名即可 const createComponent = (statePropName) => { const mapStateToProps = (state) => { return { targetData: state[statePropName] }; }; const mapDispatchToProps = (dispatch) => { return { handleAction: bindActionCreators(yourActionCreator, dispatch) }; }; return connect(mapStateToProps, mapDispatchToProps)(BaseComponent); }; // 一键生成两个组件 export const ComponentA = createComponent('a'); export const ComponentB = createComponent('b');
以后要加第三个类似的组件,直接调用createComponent('c')就行!
内容的提问来源于stack exchange,提问作者dt1000
相关产品推荐
相关产品推荐

