ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Base UI Field 组件完全指南:类型定义、校验机制与源码级 API 解析

Base UI Field 组件完全指南:类型定义、校验机制与源码级 API 解析 Base UI Field 组件完全指南类型定义、校验机制与源码级 API 解析【免费下载链接】base-uiUnstyled UI components for building accessible web apps and design systems. From the creators of Radix, Floating UI, and Material UI.项目地址: https://gitcode.com/GitHub_Trending/ba/base-uiBase UI本仓库packages/react中的base-ui/react/field的Field组件为表单控件提供了一整套无样式的标签labeling与校验validation能力它通过Field.Root统一管理字段的 dirty、touched、valid、filled、focused 等状态并可与Form组件联动完成提交校验。本文以官方 API 参考文档types.md/react/components/field/types.md)为骨架结合源码实现FieldRoot.tsx 等逐项拆解全部子组件的 Props、State、Data Attributes 与类型定义帮助你直接照表配置出一个可访问、可校验、可定制样式的表单字段。Field 组件全景七个子部件各司其职Field采用组合compoundAPI官方文档page.mdx/react/components/field/page.mdx)给出的 Anatomy 如下import { Field } from base-ui/react/field; Field.Root Field.Label / Field.Control / Field.Description / Field.Item / Field.Error / Field.Validity / /Field.Root各部分渲染的 DOM 元素与职责如下表子组件默认渲染元素职责Field.Rootdiv聚合字段所有部件Field.Itemdiv在 checkbox 组或 radio 组中将单个条目与 label、description 分组Field.Labellabel可访问标签自动与字段控件关联Field.Controlinput被标注与校验的表单控件Field.Descriptionp字段的补充说明段落Field.Errordiv校验失败时显示的错误消息Field.Validity渲染函数无 DOM根据字段有效性渲染自定义内容值得强调的是Field.Control并非必须文档明确说明可以省略它改用任意 Base UI 输入组件例如Input、Checkbox、Select等都可以开箱即用地与Field协作见 FieldControl.tsx 中的说明。Field.Root字段状态的总开关Field.Root是整个字段的中枢负责收集状态、执行校验并通过 React ContextFieldRootContext向下传递。Root PropsProp类型默认值说明namestring-表单提交时标识该字段优先级高于Field.Control上的nameactionsRefReact.RefObjectField.Root.Actions \| null-命令式操作引用validate调用时校验字段dirtyboolean-字段值是否已从初始值改变用于字段状态由外部库控制的场景touchedboolean-字段是否被触碰过用于字段状态由外部库控制的场景disabledbooleanfalse是否忽略用户交互优先级高于Field.Control上的disabledinvalidboolean-字段是否无效用于字段状态由外部库控制的场景validate((value: unknown, formValues: Form.Values) string \| void \| string[] \| Promisestring \| void \| string[] \| null \| null)-自定义校验函数。值无效时返回字符串或字符串数组作为错误消息返回空、null、空字符串或空数组表示值有效。支持异步函数但使用validationModeonSubmit时异步校验不会阻止表单提交validationModeForm.ValidationModeonSubmit决定何时校验字段优先级高于Form上的validationModevalidationDebounceTimenumber0使用validationModeonChange时两次validate回调之间的等待时间毫秒classNamestring \| ((state: Field.Root.State) string \| undefined)-元素 CSS 类名或基于组件状态返回类名的函数styleReact.CSSProperties \| ((state: Field.Root.State) React.CSSProperties \| undefined)-元素样式或基于组件状态返回样式对象的函数renderReactElement \| ((props: HTMLProps, state: Field.Root.State) ReactElement)-用其他标签替换组件的 HTML 元素或与其他组件组合Root Data AttributesAttribute类型说明data-disabled-字段禁用时存在data-valid-字段有效时存在data-invalid-字段无效时存在data-dirty-字段值已改变时存在data-touched-字段被触碰过时存在data-filled-字段有值时存在data-focused-字段控件聚焦时存在Root.Statetype FieldRootState { /** Whether the component should ignore user interaction. */ disabled: boolean; /** Whether the field has been touched. */ touched: boolean; /** Whether the field value has changed from its initial value. */ dirty: boolean; /** Whether the field is valid. */ valid: boolean | null; /** Whether the field has a value. */ filled: boolean; /** Whether the field is focused. */ focused: boolean; };注意valid的类型是boolean | nullnull表示有效性未知——例如异步validate正在执行、尚未返回结果时源码会保持中立nextState.valid null避免错误地提前放行或拦截。Root.Actionstype FieldRootActions { validate: () void };通过actionsRef可以在不触发表单交互的情况下命令式地触发一次校验。源码级解读Root 的状态管理从 FieldRoot.tsx 可以看到几个关键实现细节dirty与touched同时支持受控与非受控内部有dirtyState/touchedState但当dirtyProp/touchedProp传入时dirtyProp ?? dirtyState外部值优先setDirty/setTouched会直接跳过内部更新——这正是文档所说用于外部库控制字段状态的落地方式。disabled是disabledFieldset || disabledProp即外层Fieldset.Root禁用时字段会级联禁用。invalid的计算为invalidProp true || hasFormError外部显式传入invalid或Form中按name找到了错误都会把字段标记为无效。源码注释特别说明由invalidprop 或Form错误导致的无效在禁用状态下依然保持无效而由原生约束与validate计算出的有效性在禁用时会被抑制disabled ? null : validityData.state.valid这与原生表单中:disabled控件不参与约束校验的行为一致。每个Field.Root外层包裹了LabelableProvider配合useRenderElement(div, ...)与fieldValidityMapping生成上表所列的 Data Attributes。Field.Control默认的受控输入Field.Control默认渲染input负责把用户输入同步进字段状态并在 blur / change / Enter 等时机触发校验。Control PropsProp类型默认值说明defaultValuestring \| number \| string[]-非受控模式的初始值onValueChange((value: string, eventDetails: Field.Control.ChangeEventDetails) void)-值变化时回调受控模式下使用classNamestring \| ((state: Field.Control.State) string \| undefined)-同上基于状态返回类名styleReact.CSSProperties \| ((state: Field.Control.State) React.CSSProperties \| undefined)-基于状态返回样式renderReactElement \| ((props: HTMLProps, state: Field.Control.State) ReactElement)-替换元素或与其他组件组合Field.Control的状态类型FieldControlState与FieldRootState一致Data Attributes 也与 Root 一致data-disabled、data-valid、data-invalid、data-dirty、data-touched、data-filled、data-focused。Control.ChangeEventReason 与 ChangeEventDetailstype FieldControlChangeEventReason none; type FieldControlChangeEventDetails { /** The reason for the event. */ reason: none; /** The native event associated with the custom event. */ event: Event; /** Cancels Base UI from handling the event. */ cancel: () void; /** Allows the event to propagate in cases where Base UI will stop the propagation. */ allowPropagation: () void; /** Indicates whether the event has been canceled. */ isCanceled: boolean; /** Indicates whether the event is allowed to propagate. */ isPropagationAllowed: boolean; /** The element that triggered the event, if applicable. */ trigger: Element | undefined; };这是一个典型的 Base UI 事件详情对象BaseUIChangeEventDetails由 createBaseUIEventDetails 生成reason当前只有none一种取值cancel()可阻止 Base UI 继续处理事件allowPropagation()可在 Base UI 会阻止冒泡的场景下放行传播。源码级解读受控与非受控的分流在 FieldControl.tsx 中值通过useControlled处理valueprop 存在即受控否则走defaultValue。受控时onChange只回调onValueChange并直接 return值同步交给valueprop——注释说明消费者拒绝或改写后的值永远不会进入字段状态。非受控时onChange依次执行setDirty(inputValue ! initialValue)、setFilled(inputValue ! )、clearErrors(name)与validation.change(inputValue)。onBlur在validationMode onBlur时调用validation.commit(value)并对受控模式额外使用queueMicrotask处理 blur 时值被归一化的边界情况。onKeyDown中INPUT 上按 Enter 会setTouched(true)并触发一次 commit同时用submitCountRef判断Form是否已先处理隐式提交避免重复校验。渲染出的input带有aria-labelledby{labelId}通过getValidationProps在state.valid false !disabled时自动附加aria-invalidtrue并把 description/error 的 id 注册进aria-describedby保证屏幕阅读器可访问性。Field.Label自动关联的可访问标签Field.Label默认渲染label通过LabelableProvider的useLabel自动把htmlFor/id关联到字段控件无需手动指定。Label PropsProp类型默认值说明nativeLabelbooleantrue通过renderprop 替换元素时是否渲染原生label。若替换后的元素不是 label例如div需设为false。这在按钮类控件如Select.Trigger、Combobox.Trigger上很有用可避免继承 label 行为——包括悬停 label 时按钮出现:hover、点击 label 触发按钮点击classNamestring \| ((state: Field.Label.State) string \| undefined)-基于状态返回类名styleReact.CSSProperties \| ((state: Field.Label.State) React.CSSProperties \| undefined)-基于状态返回样式renderReactElement \| ((props: HTMLProps, state: Field.Label.State) ReactElement)-替换元素或与其他组件组合FieldLabelState与FieldRootState结构一致仅disabled会额外并入FieldItem的禁用状态Data Attributes 与 Root 相同。源码级解读nativeLabel 的运行时自检FieldLabel.tsx 在开发模式NODE_ENV ! production下会做一次 DOM 自检nativeLabel为true但渲染出的标签不是label时抛出警告非label会禁用原生标签关联htmlFor将不生效nativeLabel为false却渲染了label时同样警告label假设了原生行为而 Base UI 按非原生处理可能导致意外的指针行为。这保证了标签关联这一可访问性基础不因render替换而被悄悄破坏。Field.Description字段的补充说明Field.Description渲染p元素用于展示字段的附加说明文字例如将显示在你的个人主页上。它的 id 会被自动注册进控件的aria-describedby使辅助技术用户也能读到说明。Prop类型默认值说明classNamestring \| ((state: Field.Description.State) string \| undefined)-基于状态返回类名styleReact.CSSProperties \| ((state: Field.Description.State) React.CSSProperties \| undefined)-基于状态返回样式renderReactElement \| ((props: HTMLProps, state: Field.Description.State) ReactElement)-替换元素或与其他组件组合FieldDescriptionState与FieldRootState一致Data Attributes 与 Root 相同。其源码FieldDescription.tsx同样通过useLabelableContext()的setMessageIds将自身 id 登记为消息 id进而出现在控件的aria-describedby中。Field.Error精准控制错误消息的显示Field.Error渲染div在校验失败时显示错误消息。它最大的特点是matchprop——可以按原生ValidityState的某一项精确决定何时显示。Error PropsProp类型默认值说明matchboolean \| valid \| badInput \| customError \| patternMismatch \| rangeOverflow \| rangeUnderflow \| stepMismatch \| tooLong \| tooShort \| typeMismatch \| valueMissing-根据字段的ValidityState决定是否显示错误消息。设为true则总是显示错误消息并把可见性完全交给外部库控制classNamestring \| ((state: Field.Error.State) string \| undefined)-基于状态返回类名styleReact.CSSProperties \| ((state: Field.Error.State) React.CSSProperties \| undefined)-基于状态返回样式renderReactElement \| ((props: HTMLProps, state: Field.Error.State) ReactElement)-替换元素或与其他组件组合Error Data Attributes在 Root 的七种状态属性之外Field.Error还多出两个与动画相关的属性Attribute类型说明data-disabled-字段禁用时存在data-valid-字段处于有效状态时存在data-invalid-字段处于无效状态时存在data-dirty-字段值已改变时存在data-touched-字段被触碰过时存在data-filled-字段有值时存在data-focused-字段控件聚焦时存在data-starting-style-错误消息开始进入动画时存在data-ending-style-错误消息正在退出动画时存在Error.Statetype FieldErrorState { /** The transition status of the component. */ transitionStatus: TransitionStatus; /** Whether the component should ignore user interaction. */ disabled: boolean; /** Whether the field has been touched. */ touched: boolean; /** Whether the field value has changed from its initial value. */ dirty: boolean; /** Whether the field is valid. */ valid: boolean | null; /** Whether the field has a value. */ filled: boolean; /** Whether the field is focused. */ focused: boolean; };源码级解读match 的判定顺序与多错误渲染FieldError.tsx 中的显示逻辑rendered依次为match true→ 总是渲染字段禁用 → 不渲染match为字符串 → 渲染当且仅当validityData.state[match]为真例如matchvalueMissing只在必填项为空时显示请输入姓名其余情况 → 有Form错误或validityData.state.valid false时渲染。错误内容取自validityData.error单条或validityData.errors多条。当错误是字符串数组且多于一条时会渲染为ulli列表渲染期间通过useTransitionStatus(rendered)驱动进入/退出动画配合data-starting-style/data-ending-style即可实现 CSS 过渡。另外错误元素还会通过setMessageIds把自身 id 加入控件的aria-describedby错误消息本身对屏幕阅读器也是可访问的。Field.Validity基于有效性状态的自定义渲染Field.Validity不渲染任何 DOM它要求children是一个接收字段有效性状态的函数render prop适合需要根据校验结果渲染完全自定义 UI 的场景。Validity PropsProp类型默认值说明children*((state: Field.Validity.State) React.ReactNode)-接收字段有效性状态作为参数的函数必填Validity.Statetype FieldValidityState { /** The validity state. */ validity: { badInput: boolean; customError: boolean; patternMismatch: boolean; rangeOverflow: boolean; rangeUnderflow: boolean; stepMismatch: boolean; tooLong: boolean; tooShort: boolean; typeMismatch: boolean; valueMissing: boolean; valid: boolean | null; }; /** The transition status of the component. */ transitionStatus: TransitionStatus; errors: string[]; value: unknown; error: string; initialValue: unknown; };源码级解读稳定引用避免无效重渲染FieldValidity.tsx 中的fieldValidityState被直接交给公开的 render prop其引用身份可被消费者观察比如传给 memo 化的子组件。因此源码用React.useMemo让它在 focus、dirty、filled 等无关字段状态变化时保持稳定——只要有效性本身没变这些子组件就不会重渲染。validity字段与getCombinedFieldValidityData合并了外部invalid与本地有效性数据的结果。Field.Itemcheckbox / radio 组中的分组单元Field.Item渲染div用于在 checkbox 组或 radio 组中把单个条目连同它的 label 和 description 一起分组。它内部包裹了新的LabelableProvider使组内每个条目都能拥有独立的 label 关联。Prop类型默认值说明disabledbooleanfalse被包裹的控件是否忽略用户交互Field.Root的disabled优先级更高classNamestring \| ((state: Field.Item.State) string \| undefined)-基于状态返回类名styleReact.CSSProperties \| ((state: Field.Item.State) React.CSSProperties \| undefined)-基于状态返回样式renderReactElement \| ((props: HTMLProps, state: Field.Item.State) ReactElement)-替换元素或与其他组件组合FieldItemState与FieldRootState一致disabled合并 Root 与 Item 两层状态Data Attributes 与 Root 相同。Additional TypesField.ValidityDatatype FieldValidityData { state: { badInput: boolean; customError: boolean; patternMismatch: boolean; rangeOverflow: boolean; rangeUnderflow: boolean; stepMismatch: boolean; tooLong: boolean; tooShort: boolean; typeMismatch: boolean; valueMissing: boolean; valid: boolean | null; }; error: string; errors: string[]; value: unknown; initialValue: unknown; };Field.ValidityData是字段完整校验数据的统一载体state与原生 ValidityState 一一对应。它同时存在于 FieldRoot.tsx作为validityData状态与 useFieldValidation.ts作为校验结果中是理解整个校验流水线的核心数据结构。External TypesValidationModetype ValidationMode onSubmit | onBlur | onChange;三种校验时机的语义Field.Root的validationMode会覆盖Form上的同名 proponSubmit默认表单提交时触发校验提交后每次变更会重新校验onBlur控件失焦时触发校验onChange控件值每次变化时触发校验可配合validationDebounceTime防抖。源码级解读校验流水线与 Form 联动校验核心位于 useFieldValidation.ts几个值得注意的实现事实原生约束优先commit会先读取代表输入findRepresentativeInput的原生validity只有原生校验通过或在 onChange 校验模式下才调用自定义validate(value, formValues)。valueMissing 降噪当字段尚未被触碰markedDirtyRef.current为假时仅有的valueMissing不会立刻把字段标记为无效以减少刚聚焦就报必填的干扰一旦用户输入解决valueMissing会临时清除自定义错误让本次 onChange 通过其余原生错误留待 blur/submit 兜底。异步校验保持中立validate返回 Promise 时同步阶段先把valid置为null未知等待结果返回后再用validationCommitIdRef校验提交 ID丢弃过期结果防止竞态。多输入字段checkbox/radio 组会对同一个字段注册多个inputregisteredInputsMap校验与表单值投影都基于这份注册表且第一个当前无效的输入代表整个字段——这保证了requiredcheckbox 不会被组内其他输入顶替。与 Form 的联动commit通过getCombinedFieldValidityData把有效性数据写回formRef.current.fieldsField.Root中invalid invalidProp true || hasFormError即Form上由name关联的错误也会驱动字段进入无效状态而Form的errors会优先显示在Field.Error中。实战示例一个完整的必填姓名输入框结合官方 demotailwind/index.tsx/react/components/field/demos/hero/tailwind/index.tsx)与上面的 API 表格一个开箱即用的例子如下import { Field } from base-ui/react/field; Field.Root namename validationModeonBlur Field.LabelName/Field.Label Field.Control required placeholderRequired / Field.Error matchvalueMissingPlease enter your name/Field.Error Field.DescriptionVisible on your profile/Field.Description /Field.Root要点required由原生约束处理matchvalueMissing让错误消息只在必填缺失时出现而非任何无效状态validationModeonBlur让错误在失焦后才提示避免输入过程中频繁报错需要按字段有效状态渲染自定义 UI 时使用Field.Validity{(validity) ...}/Field.Validity使用 CSS Modules 或 Tailwind 时可直接利用data-valid/data-invalid/data-dirty等 Data Attributes 编写状态化样式无需额外 JS 状态。Export Groups 与 Canonical TypesField的命名空间导出分组如下见 index.parts.ts 与 index.tsField.RootField.Root、Field.Root.State、Field.Root.Props、Field.Root.ActionsField.LabelField.Label、Field.Label.State、Field.Label.PropsField.ErrorField.Error、Field.Error.State、Field.Error.PropsField.DescriptionField.Description、Field.Description.State、Field.Description.PropsField.ControlField.Control、Field.Control.State、Field.Control.Props、Field.Control.ChangeEventReason、Field.Control.ChangeEventDetailsField.ValidityField.Validity、Field.Validity.State、Field.Validity.PropsField.ItemField.Item、Field.Item.State、Field.Item.PropsDefaultField.ValidityData、FieldValidityData、FieldRootActions、FieldRootState、FieldRootProps、FieldLabelState、FieldLabelProps、FieldDescriptionState、FieldDescriptionProps、FieldErrorState、FieldErrorProps、FieldControlState、FieldControlProps、FieldControlChangeEventReason、FieldControlChangeEventDetails、FieldValidityState、FieldValidityProps、FieldItemState、FieldItemProps文档还给出了Canonical Types映射约定命名空间已被整体导入时优先用 Canonical 名如Field.Root.State否则用别名如FieldRootState。完整映射表Field.Root.State→FieldRootStateField.Root.Props→FieldRootPropsField.Root.Actions→FieldRootActionsField.Label.State→FieldLabelStateField.Label.Props→FieldLabelPropsField.Error.State→FieldErrorStateField.Error.Props→FieldErrorPropsField.Description.State→FieldDescriptionStateField.Description.Props→FieldDescriptionPropsField.Control.State→FieldControlStateField.Control.Props→FieldControlPropsField.Control.ChangeEventReason→FieldControlChangeEventReasonField.Control.ChangeEventDetails→FieldControlChangeEventDetailsField.Validity.State→FieldValidityStateField.Validity.Props→FieldValidityPropsField.Item.State→FieldItemStateField.Item.Props→FieldItemPropsField.ValidityData→FieldValidityData小结Field把表单字段最繁琐的标注 校验 状态同步收拢为七个职责清晰的子组件Root统管状态与校验时机Control承接输入Label/Description/Error提供开箱即用的可访问性关联Validity提供完全自由的渲染出口Item服务 checkbox/radio 分组场景。结合本仓库源码FieldRoot.tsx、useFieldValidation.ts、FieldControl.tsx、FieldError.tsx可以确认原生约束与自定义validate双层校验、异步校验竞态防护、valueMissing降噪、与Form的错误双向同步等能力都有完整实现支撑。按本文的 Props 表格与类型定义即可零障碍地接入任意 Base UI 输入组件构建兼具可访问性与自定义空间的表单体系。【免费下载链接】base-uiUnstyled UI components for building accessible web apps and design systems. From the creators of Radix, Floating UI, and Material UI.项目地址: https://gitcode.com/GitHub_Trending/ba/base-ui创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表