【发布时间】:2016-12-11 21:51:57
【问题描述】:
正在重构其中的大型项目
- 有些组件看起来非常相似
- 有大量文件(非常高级别的组件与文件粒度)
我在这里和那里思考并搜索了处理这个问题的方法,发现这篇关于高阶组件 (HOC) 的精彩文章 - 基本上是包装另一个组件的组件。
https://medium.com/@franleplant/react-higher-order-components-in-depth-cf9032ee6c3e#.8vr464t20
我现在将给你 (A) 我需要处理的八个相似组件类型中的两个示例,而不是 (B) 我将粘贴我的代码想出了将这八个文件统一为一个。最后 (C) 将粘贴该统一组件的使用示例。
我会尽量保持一致,并且命名将不受域驱动(我不能在此处发布项目详细信息),但下面不同代码摘录中的相同名称将始终指向相同的组件和数据。否则我会指出它。
(A) - 相似的组件类型
1. TabA - 简单的一个
export default class TabA extends Component {
render() {
return (
<PageWrapper>
<Grid>
<GridItem xsSize="3">
<SmartComponent something={this.props.something }/>
</GridItem>
<GridItem xsSize="9">
<Tabs
permalink = { this.props.permalink }
history={ this.props.history }
activeTab={ Paths.somePath }
/>
<TabAContent
data={ this.props.data }
name={ this.props.name }
someValue={ this.props.someValue }
/>
</GridItem>
</Grid>
</PageWrapper>
);
}
}
注意 SomeComponentA 不包含任何子元素。这里也没有任何类型的条件渲染。
2。 TabB - 更复杂的一个
同样,请注意 renderSomeData 方法有条件地渲染 SmartComponentToBeConditionallyRendered 并且 SomeComponentB 从道具中获取孩子。
export default class TabB extends Component {
renderSomeData() {
let someData = {
header: "Header text",
searchPlaceHolder: 'Search (name)',
buttonCaption: 'button caption'
};
return (
<SmartComponentToBeConditionallyRendered
type={ 'some_type' }
permalink={ this.props.permalink }
data={ someData }
/>
)
}
render() {
let { data } = this.props;
return (
<div>
<PageWrapper>
<Grid>
<GridItem xsSize="3">
<SmartComponent something={this.props.something}/>
</GridItem>
<GridItem xsSize="9">
<Tabs
permalink = { this.props.permalink }
history = { this.props.history }
activeTab = { Paths.somePage }
/>
<TabBContent data = { data }>
{this.props.children}
</TabBContent>
</GridItem>
</Grid>
</PageWrapper>
{
this.context.hasPermission('somePermission') ?
this.renderSomeData() :
null
}
</div>
)
}
static contextTypes = {
hasPermission: React.PropTypes.func.isRequired
}
}
我在开始时写过的这八个组成部分 - 它们都代表了三种可能性中的一种。 上图中的两个和可能性 C,但 C 中的差异只是另一个条件渲染组件,因此不值得一提,因为它最终会归结为在 props 中传递更多标志。
以上这两个组件 - 它们的不同之处在于:
- 一种 SomeComponentX - 在这八个相似的组件中的每一个中都可能有 A、B 和 C、D、E 等来代替 X。每个 SomeComponentX 也接受不同的道具。
- Paths.VALUE_HERE
- SomeComponentX 是否接收任何子项
- 如果他们有条件地从 renderSomeData 方法渲染数据
- 如果是 - 方法内部定义的 someData 也会发生变化
- 固定链接
- some_type
(B) 我的想法
let availablePartials = {
PartialA: PartialA,
PartialB: PartialB,
PartialC: PartialC
}
export default class GenericTab extends Component {
renderSomeData() {
return (
<SomeData
type = { this.props.type }
permalink = { this.props.permalink }
data = { this.props.someData } //PASSED FROM PROPS NOW
/>
);
}
render() {
let tabContent = React.createElement(
availablePartials[this.props.partialView.name],
this.props.partialView.props,
this.props.renderChildren ? this.props.children : null
);
return (
<div>
<PageWrapper>
<Grid>
<GridItem xsSize="3">
<SmartComponent something = { this.props.permalink }/>
</GridItem>
<GridItem xsSize = "9">
<Tabs
permalink = { this.props.permalink }
history = { this.props.history }
activeTab = { this.props.activeTab }
/>
{ tabContent }
</GridItem>
</Grid>
</PageWrapper>
{
this.context.hasPermission(this.props.requiredPermission) && this.props.dataForSomeDataMethod ?
this.renderSomeData()
: null
}
</div>
)
}
static contextTypes = {
hasPermission: React.PropTypes.func.isRequired
}
};
CityPageTab.propTypes = {
permalink: PropTypes.string,
dataForSomeDataMethod: PropTypes.object,
type: PropTypes.string,
activeTab: PropTypes.string,
renderChildren: PropTypes.bool,
partialView: PropTypes.object,
requiredPermission: PropTypes.string
};
基本上一切都是由道具构成的。我唯一不喜欢的部分是 availablePartials[this.props.partialView.name]。 它要求开发人员保持availablePartials 对象的状态一致,并且有点纠结。不是很好的解决方案,但到目前为止我想出的仍然是最好的。
(C) 新的 GenericTab 使用示例
componentThatUseGenericTabRenderMethod() {
let { valueA, valueB, valueC, history } = this.props;
let someData = {
header: 'header text',
searchPlaceHolder: 'Search (name)',
buttonCaption: 'buttonCaption'
}
return (
<GenericTab
partialView = {{
name: 'PartialA',
props: {
A: valueA,
B: valueB,
C: valueC,
history: history,
permalink: this.props.params.permalink
}
}}
permalink = { this.props.params.permalink }
activeTab = { Paths.somePath }
someData = { someData }
type = { 'SOME_TYPE' }
renderChildren = { false }
requiredPermission = { 'some_required_permision' }
/>
);
}
原来如此。用法变得有点复杂,但我删除了七个文件(删除文件是主要目标,因为它们太多了)并且将以类似的方式进一步推动它 - 通用的。 具有通用性的东西 - 它更难使用但节省了大量空间。
Project 使用 Redux,所以不要太在意将 props 向下传递。它们总是只来自一些渲染 GenericTab 的 SmartParentComponent
下面是它在页面上的外观的可视化。 GenericTab 负责渲染 Tabs 和 TabContent 部分。 是的,我知道这是一个糟糕的解决方案,但我不负责它的架构。这里有很多东西需要重构,而我所问的只是旅程中的一步。因此,请让我们专注于所提出的问题,而不是其他与此代码有很大问题的事情。我知道。:)
我想我可以用它写一篇文章,但我真的没有博客来做这件事:)。
请告诉我您的想法、建议升级、处理此问题的不同方法等。
【问题讨论】:
-
您能否添加一些关于选项卡外观的可视化表示?在这里理解整个事情有点困难,但我已经可以告诉你,你的组件负责渲染很多东西,你应该把它分成几个更小的组件。
-
hm,对我来说唯一有意义的拆分就是加入
renderSomeData。我可以提取它,这是真的。除此之外,我在这里看不到很多可能性。项目很混乱,还有很多事情要做。是的,我会尝试编辑我的问题并添加可视化效果。 -
我相信这有点可能,当您编辑您的问题时,我想我可以帮助您这样做
-
@Pcriulan done: ) 我还更改了代码中的命名,因此它现在类似于图像中的命名。我知道逻辑是扭曲的,因为在每个选项卡中都有一个超级奇怪的选项卡实现,但将来会改变。现在我只是在想是不是太多了,从道具中渲染出这么多东西(甚至要渲染的组件名称也在道具中动态传递)等等。
-
好多了!我猜
GenericTab是主页包装内容吧?
标签: javascript inheritance reactjs