前言
最近开始学习React,跟着Kent学,有很多干货,这里分享Rect中的一个模式或者方法getProps
这里还是讨论props传递的问题,之前React中的设计模式 - 组合组件(上) 以及React中的设计模式 - 组合组件(下) 已经讨论过很多模式,本文讨论getProps
让组件使用者调用props, 而不是被动从父组件接受props
文章中完整的示例代码可以查看 这里
一、background
之前讨论的几种设计模式,本质上都是父组件给子组件传递属性,子组件没有获取属性的主动权,很多时候组件使用者其实更清楚需要什么属性,把属性的获得权交给使用者往往更容易给组件扩展新功能
于是,不妨设计出以下API,component可以通过getProps的主动获取父组件属性
<Component {...getProps({property_A, property_b})} />
二、原型
2.1 example
为了getProps的使用,不妨使用tonggle组件的例子
其中,App设计大概如下,on是组件的开关,
- Switch组件需要获取on属性
- button组件需要重写onClick属性,并且加入aria-label以及id的属性
function useToggle() {
const [on, setOn] = React.useState(false)
let toggle = () => setOn(!on)
const getTogglerProps = (params) => {
// 待实现
}
}
return {on, toggle, getTogglerProps}
}
function App() {
const {on, getTogglerProps} = useToggle()
return (
<div>
<Switch {...getTogglerProps({on})} />
<hr />
<button
{...getTogglerProps({
'aria-label': 'custom-button',
onClick: () => console.info('onButtonClick'),
id: 'custom-button-id',
})}
>
{on ? 'on' : 'off'}
</button>
</div>
)
}
2.2 实现
根据以上的要求,getTogglerProps的实现就是获得从用户中获得属性,如果用户没有提供属性,那么就使用default的属性,于是实现如下
const getTogglerProps = (params) => {
if (params.onClick) {
toggle = () => {
setOn(!on)
params.onClick()
}
}
return{
'aria-pressed': on,
...params,
onClick: toggle
}
}
可以注意到,对于onClick的实现是略有复杂,具体就是因为onClick优惠default的方法,如果用户也提供了方法,不用把default的方法覆盖掉,更好的做法是执行defualt的方法,并且执行用户传过来的方法
2.3 改进
上面对onClick方法的实现并没有很通用,能不能有更加通用的方法呢,比如实现一个方法传入default的方法以及用户的方法,就能同时执行呢?
onClick: callAll(onClick, toggle),
是可以有的,实现callAll其实不算太复杂
const callAll = (...fns) => (...args) => fns.forEach(fn => fn?.(...args))
于是,全部代码如下
import * as React from 'react'
import {Switch} from '../switch'
const callAll = (...fns) => (...args) => fns.forEach(fn => fn?.(...args))
function useToggle() {
const [on, setOn] = React.useState(false)
const toggle = () => setOn(!on)
function getTogglerProps({onClick, ...props} = {}) {
return {
'aria-pressed': on,
onClick: callAll(onClick, toggle),
...props,
}
}
return {
on,
toggle,
getTogglerProps,
}
}
function App() {
const {on, getTogglerProps} = useToggle()
return (
<div>
<Switch {...getTogglerProps({on})} />
<hr />
<button
{...getTogglerProps({
'aria-label': 'custom-button',
onClick: () => console.info('onButtonClick'),
id: 'custom-button-id',
})}
>
{on ? 'on' : 'off'}
</button>
</div>
)
}
export default App
总结
getProps是一个很好的的设计理念,可以让用户拥有更多的主动权,这样设计出来的组件也会更加通用;在设计组件的时候,如果是通用型组件,不妨提供getProps的方法