为React组件编写API,第1部分:不要创建冲突的道具
编写用于React组件的API,第2部分:为行为命名,而非交互
为React组件编写API,第3部分:道具的顺序很重要
编写用于React组件的API,第4部分:当心提示!
编写用于React组件的API,第5部分:使用组合
我们为React组件编写API,第6部分:在组件之间创建通信
我们有一个switch组件Switch
,它接受prop,现在我们称它为something
。
使用我们组件的开发人员可以传递一个函数,当值改变时我们将调用它。

<Switch something={fn} />
React使我们能够clickHandler
调用prop: handler
/ clickHandler
/ onClick
/ onToggle
等。
事件处理程序的名称应以on
开头的约定(例如onClick
) onClick
。 这是因为HTML规范具有许多已经遵循此约定的处理程序: onkeydown
, onchange
, onclick
等。
重用现有协议是一个好主意;开发人员将不必记住新的东西。
好的,那onClick
呢?
<Switch onClick={fn} />
在这里,我不是名称onClick
的支持者,该名称表明单击鼠标是与该组件进行交互的唯一方法。
移动设备上的用户可以用手指按下开关或将其向右拖动。 视障用户可以将其与屏幕阅读器软件配合使用,也可以使用键盘键。
作为使用此组件的开发人员,我不想考虑最终用户如何与该组件交互。 我只想附加一个在值更改时调用的函数。
为您的API提供名称,该名称不会指示交互方式:
<Switch onToggle={fn} />
那是有道理的,对吧? 在两个值之间toggles
(切换)。
在组件内部,您可能希望代理同一功能中的所有可能的交互
function Switch(props) { return ( <div className="switch" /* */ onClick={props.onToggle} onKeyDown={function(event) { /* enter , event */ if (event.key === 'Enter') props.onToggle(event) }} onDrag={function(event) { /* */ if (event.toElement === rightSide) props.onToggle(event) }} /> ) }
我们考虑了所有选项,以便为用户(开发人员)提供清晰的API。
现在让我们谈谈文本输入组件。

<TextInput />
HTML具有onchange属性 , React文档在其示例中使用onChange
。 显然,这个分数已经达成共识。
<TextInput onChange={fn} />
很简单
现在让我们将这两个组件并排放置。

<TextInput onChange={fn} /> <Switch onToggle={fn} />
看到奇怪的东西了吗?
尽管两个组件都需要相同的行为,但是prop的调用方式有所不同。 这些道具非常适合它们各自的组件,但是当您一起查看组件时,它看起来很有争议。
使用这些组件的开发人员将始终必须在使用道具名称之前对其进行检查。
所以这里是技巧2: 在组件之间争取一致的道具 。 对于所有组件,相同的行为应具有相同的属性。
该建议也可以制定如下: 争取最小的API面积 。 您必须限制开发人员必须精通的API数量,才能进行高效的工作。
我想说的是,该主题的所有优点都归塞巴斯蒂安·马克伯 ( SebastianMarkbåge)所为 。 (他的演讲: 最小的API表面积 )
实施此建议的方法是选择一个道具并在所有组件中使用它。 在我们的示例中拥有的两个道具中, onChange
也在HTML规范中,因此某些开发人员可能会认识到它。

<TextInput onChange={fn} /> <Switch onChange={fn} /> <Select onChange={fn} /> // etc.
组件之间的一致性以及因此学习API的简便性胜过为单个组件选择“理想”道具。
一小笔奖金。
让我们谈谈此函数的签名。
<TextInput onChange={fn} />
onChange
事件onChange
(在此示例中为fn
)接收一个参数event
。
它适用于所有更改。 您可以从此事件中获得很多有用的信息。
function fn(event) { console.log(event.target)
最有可能的是,大多数开发人员会对event.target.value
感兴趣,以便他们可以将其用于其他一些任务-用于状态,提交表单等。
就我们的Switch
组件而言,每个动作代表一个单独的“事件” event
。 此event
将具有不同的单击和拖动属性。 我们如何确保API一致?
我们可以为每个“事件”手动设置event.target.value
:
function Switch(props) { const fireHandler = event => { const newValue = !oldValue event.target.value = newValue props.onChange(event) } return ( <div className="switch" /* */ onClick={fireHandler} onKeyDown={function(event) { if (event.key === 'Enter') fireHandler(event) }} onDrag={function(event) { if (event.toElement === rightSide) fireHandler(event) }} /> ) }