业务组件只需要跟对应的业务去写对应的功能即可,不用考虑不同场景的兼容性;通用型组件,例如著名的ant design就是通用型业务解决方案组件,能兼容不同场景,可以在不同需求上面使用。个人总结的几个经验
1、多种模式下,使用枚举
你写了一个Tabs组件,默认tab位置在顶部:
这时有人在另一个页面上需要使用你的组件,提了一个需求,tab需要展示在底部,于是你增加了一个属性
bottom={true}
,实现了:
<Tabs bottom={true}> </Tabs>
这时又有人提出,tab需要在左侧展示,于是你又增加了一个属性
left={true}
,
<Tabs left={true}> </Tabs>
很完美,同样实现了这个功能。
但是这时候有一个问题,如果同时写了
left={true} 、bottom={true}
,该如何展示呢。这种设计方法,容易让使用者混淆。最好的办法使用枚举替代,举例出几种可能的情况,让使用者从中选择,
position= 'left' | 'bottom' | 'top' | 'right'
这样在使用时候从中选择对应枚举的值即可实现不同需求的同时避免混淆:
<Tabs position='top'> </Tabs> // 顶部
<Tabs position='bottom'> </Tabs> // 底部
<Tabs position='left'> </Tabs> // 左侧
<Tabs position='right'> </Tabs> // 右侧
2、巧用组合模式
以antd的tabs组件为例子,如果普通实现
bad:
<TabPan active={true} onclick={this.onclick}> 1111 </TabPan>
<TabPan active={false} onclick={this.onclick}> 222</TabPan>
<TabPan active={false} onclick={this.onclick}> 333</TabPan>
我们发现这是一种不合理的做法,要给每个tab标签传递是否激活、点击事件。
good:
使用组件克隆,见官方文档:https://zh-hans.reactjs.org/docs/react-api.html#cloneelement
React.cloneElement(
element,
[config],
[...children]
)
第一个参数是react的节点
第二个参数是节点配置,相当于<div { ...config}> </div>
第三个参数,可以覆盖前面传入的节点以及内容
50行代码实现同等antd的核心api调用方式:
import React, {Component} from 'react';
class Tabs extends Component {
state = {
activeKey: this.props.defaultActiveKey
}
onChange = (activeKey) => {
this.setState({
activeKey
})
this.props.onChange(activeKey)
}
render() {
return (
<div>
{this.props.children.map((child, index) => {
return React.cloneElement(child, { // 通过克隆可以修改子组件的值
...child,
active: child.props.id === this.state.activeKey,
key: child.props.id,
activeKey: this.state.activeKey,
onChange: () => this.onChange(child.props.id)
})
})}
</div>
);
}
}
class TabPane extends Component {
render() {
return (
<div>
<h1 style={{color: this.props.active && 'red'}} onClick={this.props.onChange}>
{this.props.tab}
</h1>
<div> {(this.props.activeKey === this.props.id) && this.props.children}</div>
</div>
);
}
}
使用:
同官方api使用一样,两个核心api:defaultActiveKey
、onChange
import React, {Component} from 'react';
class Index extends Component {
callback = (key) => {
console.log('key', key)
}
render() {
return (
<div>
<Tabs defaultActiveKey={1} onChange={this.callback}>
<TabPane id={1} tab='tab-1'>
Content of Tab Pane 111
</TabPane>
<TabPane id={2} tab='tab-2'>
Content of Tab Pane 2222
</TabPane>
<TabPane id={3} tab='tab-3'>
Content of Tab Pane 333
</TabPane>
</Tabs>
</div>
);
}
}
原来我们调用需要:
<TabPan active={true} onclick={this.onclick}> 1111 </TabPan>
现在我们只需要:
<TabPan > 1111 </TabPan>
3、移交控制权-提高组件灵活度
1、少些行内样式,能用classname解决的样式就不用行内样式
因为行内样式的优先级太高了,组件封装时候,用户在外界无法通class样式覆盖行内样式。当然可以设计成通过传入style来混合样式,但是如果两三个以上的话造成有点混乱,这是很差的一种组件封装。
2、给使用者传递ReactNode进来,是最好的选择。
举个例子:
一个指标组件,开发者根据textarea字段传入数组生成对应的区块指标
import React from 'react';
import { Interval } from 'open-library';
export default () => <Interval textarea={['低','中','高']}/>;
需求1:我想修改字体颜色,那么可以很快通过找到classname类名来改变:
需求2:如果此时,我想修改字体低、中、高三个从依次从小到大的字体大小,这就比较操蛋了。
所以最好的解决方式,就是移交控制权给用户,即这个展示文字可以由用户去自定义配置修改,组件内部不用管。
修改组件textarea字段后使用:
import React from 'react';
import { Interval } from 'open-library';
export default () => <Interval
textarea={[<span style={{fontSize:14}}>低</span>,
<span style={{fontSize:20}}>中</span>,
<span style={{fontSize:26}}>高</span>]}
/>;
此时我们通过移交控制权给用户,用户可以完全写各种需求传递给组件,组件的灵活度就提高了。
网友评论