前言
最近开始学习React,跟着Kent学,有很多干货,这里分享Compound Components组合组件的第二种模式
文中完整的示例代码可以查看 这里
一、background
之前在React中的设计模式 - 组合组件(上)中,我们提到可以通过cloneElement的方法修改给子组件加属性,从而不需要通过props向下传递,但是如果遇组件跟DOM元素嵌套之后,这个方法就不能用了,情况如下
function App() {
return (
<div>
<Toggle>
<ToggleOn>The button is on</ToggleOn>
<ToggleOff>The button is off</ToggleOff>
<div>
<ToggleButton />
</div>
</Toggle>
</div>
)
}
ToggleButton被div包裹导致ToggleButton无法被Toggle clone并且被添加属性
二、解决办法
2.1 初始方案
不妨使用Context处理多组件嵌套属性传递的问题,具体来说就是Toggle其实返回一个context provider,如下
const ToggleContext = React.createContext()
function Toggle({children}) {
const [on, setOn] = React.useState(false)
const toggle = () => setOn(!on)
return (
<ToggleContext.Provider value={{on, toggle}}>
{children}
</ToggleContext.Provider>
)
}
2.2 使用hook
既然提供了ToggleContext,一般的最佳实践会通过hook向外提供统一的context获取方法,具体可以参考这篇文章React中Context的使用方法
const useToggle = () => {
const context = React.useContext(ToggleContext)
if (!context) throw new Error('no context ')
return context
}
2.3 子组件获取属性
基本用法就是调用前面的hook,获得context
function ToggleOn({children}) {
const context = useToggle()
const {on} = context
return on ? children : null
}
function ToggleOff({children}) {
const context = useToggle()
const {on} = context
return on ? null : children
}
function ToggleButton(props) {
const context = useToggle()
const {on, toggle} = context
return <Switch on={on} onClick={toggle} {...props} />
}
以上的方法也是通让可以避免组件props多层级传递,并且useContext的很多逻辑也封装得很好,只要注意使用ToggleOn ToggleOff或者ToggleButton的时候用Toggle组件包裹一下就可以了
如果用过antd这个Libray,里面Form和Form.Item跟这里的用法就很类似,Form.Item使用的时候需要被Form包裹
总结
更好传递props会使得代码更容易被使用,当多层级传递props的时候不妨考虑使用Compound Components组合组件的的方法