冲突
冲突
在上一课的“切换”练习中,我们看到了如果存在冲突,将所有 props 传递下去可能会导致一些问题。
我们来看一个最简示例:
这个 Checkbox 组件会给 <input> 应用两个硬编码属性: type 和 id 。
现在,假设该组件的使用者如下使用它:
type 和 onClick 属性并未在 Checkbox 组件中指定,因此它们会被收集到 delegated 对象中,并被应用到 <input> 上:
我们为 type 指定了两个不同的值,在遇到此类冲突时,后面的值会覆盖前面的值。因此,这个输入框最终会是一个按钮而不是复选框。
本质上,使用者“黑”了我们的 Checkbox 组件,让它不再渲染成复选框!
让我们重写 Checkbox 组件,优先展开提供的 props:
通过这个更改,相同的 <Checkbox> 元素会产生不同的结果:
因为我们调换了顺序,用户提供的 type="button" 现在会被内置的 type="checkbox" 覆盖。
API 设计中的一个强大工具
在编写 React 组件时,我们可以决定赋予使用者多大的控制权。我们可以选择允许他们覆盖哪些属性,以及哪些属性是必填的或固定不变的。
在上面的例子中,我非常倾向于让一个 Checkbox 组件始终渲染一个 <input type="checkbox"> ,因此我不想让消费者覆盖 type 属性。
但情况并非总是如此!有时,我确实希望允许用户覆盖内置属性。
例如,假设我有一个生成 SVG 图标的组件:
默认情况下,这个组件会渲染一个带有圆角线条的黑色箭头,但我可以提供自己的覆盖样式:
对于 {...delegated}
应该放在哪里,并没有对或错的答案。相反,这是一项我们可以利用的工具,用于决定我想给予使用此组件的开发人员多少权限和灵活性。
手动管理冲突
有时候,委托属性这个工具过于粗糙,我们需要做一些手动工作来解决冲突。
例如,在处理 CSS 类时,我们通常希望同时应用用户提供的类和内置的类。
在 Toggle 练习中,我们手动将两个类合并在一起,以便应用 toggle 类(提供所有标准 toggle 样式)以及 green-toggle 类(用户指定的类,用于覆盖 toggle 的颜色)。
我构建了许多遵循此精确模板的组件。以下是一个最小可行示例,所有其他内容都被移除:
从某种意义上说,我们其实已经见过这种模式的一个例子了,那就是在我们讨论 Hook 的规则时:
如果用户提供了 id 属性,它将用于输入框的 id ,以及标签的 htmlFor 。如果没有提供,我们将使用从 React.useId 钩子(hook)中获得的生成值。
我们可以依赖 rest/spread 操作符在 <input> 上应用正确的 id ,但我们还需要通过 htmlFor 属性在 <label> 上设置完全相同的值。因此,我们需要手动处理这一冲突。
下面还有一个示例,展示如何向已经具有某些内联样式的组件提供自定义的内联样式:
回顾一下,在处理属性冲突时,我们有几种不同的选择。
- 如果我们希望允许使用者覆盖某个特定的硬编码属性,可以在其后使用 {...delegated} 语法。
- 然而,如果我们希望优先使用硬编码属性,则应将 {...delegated} 语法放在前面。
- 如果我们想要合并两个值,则需要我们自己进行管理,这时不使用 {...delegated} 。
这三种选项在不同情况下都是有效的。具体取决于我们想给予使用者多少控制权。