前言
受控组件需要主动维护一个内部的state状态,但是有些时候value和onChange的维护十分麻烦,尤其是需要维护多套状态值的情况下,比如form表单,tabs选项卡等等。对于他们的每一个单项的组件来说,完全没有必要单独维护自己的state,所以为什么不试试组件组合了,亲~
举个栗子
一个受控的Tabs组件可能是这么写的。。。
interface TabBarProps {
/**
* 标签页
* @type {string[]}
* @memberof TabBarProps
*/
data: string[]
/**
* 选中
* @type {number}
* @memberof TabBarProps
*/
selectedIndex: number
/**
* 改变事件 如果selectedIndex 未改变不触发
* @memberof TabBarProps
*/
onChange?: (index: number) => void
}
...
render() {
const { data, selectedIndex } = this.props;
return <View className={styles.content}>
{data.map((value, index) =>
<View key={value + index} className={classNames
(styles.item, selectedIndex === index ? styles.selected : {})}
onClick={() => this.onClick(index)}>
{value}
</View>)}
</View>
}
private onClick(index) {
const { selectedIndex, onChange } = this.props;
if (selectedIndex !== index) {
onChange && onChange(index);
}
}
对于上述组件来说,selectedIndex需要自己维护吗,在开发中我们确实需要知道selectedIndex的变化来请求数据或者其他的操作,但是点击切换的动作我们希望由组件自己完成。其次tabs组件的自由度是非常高的,我们希望不因为一些UI上的变动去修改组件,换言之,组件只帮你处理逻辑问题。
Tab+TabItem 的组合使用
//Tab
export interface TabProps extends React.PropsWithChildren<object> {
/**
* change事件
*/
onChange?: (index: number) => void
}
export default memo(function Index(props: TabProps) {
const [activeIndex, setActiveIndex] = useState(0);
function tabsChange(index: number) {
setActiveIndex(index)
props.onChange && props.onChange(index)
};
const newChildren = React.Children.map(props.children, (child: any, index) => {
if (child.type) {
return React.cloneElement(child, {
active: activeIndex === index,
onClick: () => tabsChange(index)
})
} else {
return child
}
})
return (
<div>
{newChildren}
</div>
)
})
//TabItem
export default memo(function Index(props: any) {
const { active, onClick, children } = props;
const style = {
color: active ? '#0156A9' : '#000'
}
return (
<div style={style} onClick={onClick} >
{children}
</div>
)
})
对于TabItem组件或者Tab的子组件来说,只需要具备active属性和onClick事件即可,所有的状态逻辑都提取在Tab组件中,子组件是纯傻瓜式组件,通过React.Children.map来对props.children进行遍历。
所以组合组件的使用更加灵活,不会因为UI上的变动去修改组件,同时Tab组件本身也维护了自己的state,通过onChange事件将变化的index传递出去。
<Tab>
<TabItem>第一</TabItem>
<TabItem>第二</TabItem>
<TabItem>第三</TabItem>
</Tab>
总结
虽然大部分时候都推荐使用受控组件,但是也需要避免产生不必要的state,相较于Context,React.children提供了一种不打破组件props传递的很好的实践方式。