React 钩子
Payload 提供了多种强大的 React Hooks,可以在你自己的自定义组件中使用,例如自定义字段。通过这些 Hooks,你可以与 Payload 本身进行交互,构建几乎任何你能想到的复杂自定义功能。
提醒: 所有自定义组件默认都是 React Server Components。而 Hooks 仅在客户端环境中可用。要使用 Hooks,请确保你的组件是客户端组件。
useField
useField
钩子在所有字段组件内部使用。它负责管理与父表单之间的字段状态收发。当你构建自定义字段组件时,你需要自行处理字段value
与表单之间的收发。
使用时按如下方式导入useField
钩子:
'use client'
import type { TextFieldClientComponent } from 'payload'
import { useField } from '@payloadcms/ui'
export const CustomTextField: TextFieldClientComponent = ({ path }) => {
const { value, setValue } = useField({ path }) // highlight-line
return (
<div>
<p>{path}</p>
<input
onChange={(e) => {
setValue(e.target.value)
}}
value={value}
/>
</div>
)
}
useField
钩子接受以下参数:
Property | Description |
---|---|
path | 如果不提供path ,则会使用name 。这是字段在表单数据中的路径。 |
validate | 在表单提交到服务器_之前_执行的客户端验证函数。与字段级验证不同,后者仅在服务器端运行。 |
disableFormData | 如果为true ,提交表单时该字段不会包含在表单数据中。 |
hasRows | 如果为true ,该字段将被视为包含行的字段。这对array 和blocks 等字段类型很有用。 |
useField
钩子返回以下对象:
type FieldType<T> = {
errorMessage?: string
errorPaths?: string[]
filterOptions?: FilterOptionsResult
formInitializing: boolean
formProcessing: boolean
formSubmitted: boolean
initialValue?: T
path: string
permissions: FieldPermissions
readOnly?: boolean
rows?: Row[]
schemaPath: string
setValue: (val: unknown, disableModifyingForm?: boolean) => void
showError: boolean
valid?: boolean
value: T
}
useFormFields
有时自定义字段组件需要访问其他字段的数据,你有几种实现方式。useFormFields
钩子提供了一种高效且强大的方法来获取表单字段状态,同时还能获取 dispatchFields
方法,这对于在表单任意位置设置其他字段的表单状态非常有用。
此钩子非常适合仅获取表单状态中的特定字段 因为它确保只在你请求的字段发生变化时才会触发重新渲染。
得益于优秀的 use-context-selector
包,你可以轻松获取特定字段的状态。这种方式非常理想,因为你既能确保获取到最新的字段状态,又只会在_该字段状态_变化时触发组件重新渲染。
你可以向钩子传入类似 Redux 的选择器函数,确保只获取你需要的字段。选择器函数接收类型为 [fields: Fields, dispatch: React.Dispatch<Action>]]
的参数。
'use client'
import { useFormFields } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// 仅获取 `amount` 字段状态,且仅在该字段变化时触发重新渲染
const amount = useFormFields(([fields, dispatch]) => fields.amount)
// 同上,但针对 `feePercentage` 字段
const feePercentage = useFormFields(
([fields, dispatch]) => fields.feePercentage,
)
if (
typeof amount?.value !== 'undefined' &&
typeof feePercentage?.value !== 'undefined'
) {
return <span>费用为 ${(amount.value * feePercentage.value) / 100}</span>
}
}
useAllFormFields
要获取多个字段,你可以使用 useAllFormFields
hook。当_任何_字段发生变化时,你的组件都会重新渲染,因此请仅在绝对必要时使用此 hook。与 useFormFields
hook 不同,此 hook 不接受"选择器",它总是返回一个类型为 [fields: Fields, dispatch: React.Dispatch<Action>]]
的数组。
通过获取完整的表单状态,你可以实现许多强大的功能,例如使用内置的辅助函数将字段状态简化为仅包含值,或者通过路径获取兄弟数据。
'use client'
import { useAllFormFields } from '@payloadcms/ui'
import { reduceFieldsToValues, getSiblingData } from 'payload/shared'
const ExampleComponent: React.FC = () => {
// `fields` 常量将等于所有字段的状态,
// `dispatchFields` 方法可用于将字段状态发送到表单
const [fields, dispatchFields] = useAllFormFields();
// 传入字段,并指定是否要"解扁平化"字段数据。
// 下面的结果将反映给定时刻表单中存储的数据
const formData = reduceFieldsToValues(fields, true);
// 传入字段状态和路径,
// 你将获得指定路径的所有兄弟数据
const siblingData = getSiblingData(fields, 'someFieldName');
return (
// 如有必要,在此返回一些 JSX
)
};
更新其他字段的值
如果你正在构建自定义组件(Custom Component),那么应该使用 useField
钩子返回的 setValue
来以编程方式设置字段值。但如果你需要更新 其他 字段的值,可以使用 useAllFormFields
返回的 dispatchFields
。
你可以向 dispatchFields
函数发送以下操作:
Action | 描述 |
---|---|
ADD_ROW | 添加一行数据(适用于数组/区块字段数据) |
DUPLICATE_ROW | 复制一行数据(适用于数组/区块字段数据) |
MODIFY_CONDITION | 更新字段的条件逻辑结果(true/false) |
MOVE_ROW | 移动一行数据(适用于数组/区块字段数据) |
REMOVE | 从表单状态中移除字段 |
REMOVE_ROW | 从表单状态中移除一行数据(适用于数组/区块字段数据) |
REPLACE_STATE | 完全替换表单状态 |
UPDATE | 更新特定字段状态的任何属性 |
要查看 dispatchFields
钩子支持的每种操作的类型定义,请查看 Form 类型定义。
useForm
useForm
钩子可用于与表单本身进行交互,并返回许多方法,这些方法可以用于响应式获取表单状态,而不会在每次字段更改时导致组件重新渲染。这对于具有基于动作的回调函数的组件非常有用,这些回调函数需要基于用户动作与表单状态进行交互。
警告:
此钩子经过优化,避免在字段更改时导致重新渲染,因此其 fields
属性可能会过时。只有在需要根据用户操作对表单执行操作时才应使用此钩子。不要依赖其返回的 "fields" 作为最新状态。在未来的版本中,这些字段将从该钩子的返回值中移除。
useForm
钩子返回一个包含以下属性的对象:
操作 | 描述 | 示例 | |
---|---|---|---|
fields | 已弃用。此属性不能作为最新状态依赖。 | ||
submit | 触发表单提交的方法 | ||
dispatchFields | 向表单字段状态分发动作 | ||
validateForm | 触发表单状态验证 | ||
createFormData | 从当前表单状态创建 `multipart/form-data` 对象 | ||
disabled | 表示表单是否禁用的布尔值 | ||
getFields | 从状态获取所有字段 | ||
getField | 通过路径从状态获取单个字段 | ||
getData | 返回表单中存储的数据 | ||
getSiblingData | 返回给定字段路径的表单兄弟数据 | ||
setModified | 设置表单的 `modified` 状态 | ||
setProcessing | 设置表单的 `processing` 状态 | ||
setSubmitted | 设置表单的 `submitted` 状态 | ||
formRef | 表单 HTML 元素的引用 | ||
reset | 将表单重置为初始状态的方法 | ||
addFieldRow | 向数组或块字段添加行的方法 | ||
removeFieldRow | 从数组或块字段移除行的方法 | ||
replaceFieldRow | 替换数组或块字段中行的方法 |
useDocumentForm
useDocumentForm
钩子与 useForm 钩子的工作方式相同,但它始终提供对文档顶层 Form
的访问权限。当你需要从子 Form
中访问文档的 Form
上下文时,这非常有用。
一个典型的应用场景是在 lexical blocks 中的自定义组件,因为 lexical blocks 会初始化自己的子 Form
。
'use client'
import { useDocumentForm } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
const { fields: parentDocumentFields } = useDocumentForm()
return (
<p>
该文档的 Form 包含 ${Object.keys(parentDocumentFields).length} 个字段
</p>
)
}
useCollapsible
useCollapsible
钩子允许你控制父级可折叠组件:
Property | Description |
---|---|
isCollapsed | 可折叠组件的状态。展开时为 true ,折叠时为 false 。 |
isVisible | 如果是嵌套结构,判断最近的可折叠组件是否可见。当没有父级关闭时为 true ,否则为 false 。 |
toggle | 切换最近可折叠组件的状态。 |
isWithinCollapsible | 判断当前是否处于另一个可折叠组件内。 |
示例:
'use client'
import React from 'react'
import { useCollapsible } from '@payloadcms/ui'
const CustomComponent: React.FC = () => {
const { isCollapsed, toggle } = useCollapsible()
return (
<div>
<p className="field-type">我当前是{isCollapsed ? '收起' : '展开'}状态</p>
<button onClick={toggle} type="button">
切换
</button>
</div>
)
}
useDocumentInfo
useDocumentInfo
钩子提供了当前正在编辑文档的相关信息,包括以下内容:
属性 | 描述 |
---|---|
action | 底层表单元素上附加的 action 属性 URL,指定表单提交时发送数据的目标地址。 |
apiURL | 当前文档的 API URL。 |
collectionSlug | 如果正在编辑 collection 文档,则返回该 collection 的 slug。 |
currentEditor | 当前正在编辑文档的用户。 |
docConfig | 根据编辑内容类型返回 Collection 或 Global 的配置。 |
docPermissions | 当前文档的权限。当不存在 id 时回退到 collection 权限。 |
documentIsLocked | 文档是否被其他用户锁定。更多详情。 |
getDocPermissions | 获取文档级别权限的方法。 |
getDocPreferences | 获取文档级别用户偏好设置的方法。更多详情。 |
globalSlug | 如果正在编辑 global 文档,则返回该 global 的 slug。 |
hasPublishedDoc | 文档是否有已发布的版本。 |
hasPublishPermission | 当前用户是否有发布文档的权限。 |
hasSavePermission | 当前用户是否有保存文档的权限。 |
id | 如果文档是 collection,则返回其 ID。 |
incrementVersionCount | 增加文档版本计数的方法。 |
initialData | 文档的初始数据。 |
isEditing | 文档是否处于编辑状态(与创建相对)。 |
isInitializing | 文档信息是否仍在初始化中。 |
isLocked | 文档是否被锁定。更多详情。 |
lastUpdateTime | 文档最后更新的时间戳。 |
mostRecentVersionIsAutosaved | 最新版本是否为自动保存的版本。 |
preferencesKey | 与文档级别用户偏好设置交互时使用的 preferences 键。更多详情。 |
savedDocumentData | 文档的已保存数据。 |
setDocFieldPreferences | 为特定字段设置偏好设置的方法。更多详情。 |
setDocumentTitle | 设置文档标题的方法。 |
setHasPublishedDoc | 更新文档是否已发布状态的方法。 |
title | 文档的标题。 |
unlockDocument | 解锁文档的方法。更多详情。 |
unpublishedVersionCount | 文档未发布版本的数量。 |
updateDocumentEditor | 更新当前编辑文档用户的方法。更多详情。 |
updateSavedDocumentData | 更新已保存文档数据的方法。 |
uploadStatus | 进行中上传的状态('idle'、'uploading' 或 'failed')。 |
versionCount | 文档当前的版本计数。 |
示例:
'use client'
import { useDocumentInfo } from '@payloadcms/ui'
const LinkFromCategoryToPosts: React.FC = () => {
// highlight-start
const { id } = useDocumentInfo()
// highlight-end
// 在创建表单上 id 会是 undefined
if (!id) {
return null
}
return (
<a
href={`/admin/collections/posts?where[or][0][and][0][category][in][0]=[${id}]`}
>
查看文章
</a>
)
}
useListQuery
useListQuery
钩子用于订阅 List View 中使用的数据、当前查询和其他属性。你可以在 List View 内渲染的任何自定义组件中使用这个钩子。
'use client'
import { useListQuery } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const { data, query } = useListQuery()
// highlight-end
// ...
}
useListQuery
钩子返回一个包含以下属性的对象:
属性 | 描述 |
---|---|
data | 当前在 List View 中显示的数据。 |
defaultLimit | List View 中默认显示的条目数量限制。 |
defaultSort | List View 中条目的默认排序顺序。 |
handlePageChange | 处理 List View 中页面变更的方法。 |
handlePerPageChange | 处理 List View 中每页显示数量变更的方法。 |
handleSearchChange | 处理 List View 中搜索变更的方法。 |
handleSortChange | 处理 List View 中排序变更的方法。 |
handleWhereChange | 处理 List View 中 where 条件变更的方法。 |
modified | 表示查询是否已从其查询预设发生变更。 |
query | 当前用于获取 List View 数据的查询参数。 |
useSelection
useSelection
钩子提供了 List 视图中选中行的信息,以及简化选择操作的辅助方法。该钩子返回一个包含以下属性的对象:
属性 | 描述 |
---|---|
count | 当前选中行的数量。 |
getQueryParams | 根据当前选择状态和可选附加过滤参数生成查询字符串的函数。 |
selectAll | 表示选择范围的枚举值:'allAvailable' 、'allInPage' 、'none' 和 'some' 。导出 SelectAllStatus 枚举以便于比较。 |
selected | 以文档 ID 为键、布尔值为值的映射,表示各文档的选择状态。 |
setSelection | 切换文档行选择状态的函数。 |
toggleAll | 切换当前页所有文档选择状态的函数,当传入 true 时会选择所有可用文档。 |
totalDocs | 集合中的文档总数。 |
示例:
'use client'
import { useSelection } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const { count, toggleAll, totalDocs } = useSelection()
// highlight-end
return (
<>
<span>
已选中 {count} 个文档(共 {totalDocs} 个)!
</span>
<button type="button" onClick={() => toggleAll(true)}>
全选/取消全选
</button>
</>
)
}
useLocale
在任何自定义组件中,你都可以使用 useLocale
钩子获取当前选中的语言环境对象。useLocale
会返回完整的语言环境对象,包含 label
、rtl
(从右到左)属性以及 code
。下面是一个简单示例:
'use client'
import { useLocale } from '@payloadcms/ui'
const Greeting: React.FC = () => {
// highlight-start
const locale = useLocale()
// highlight-end
const trans = {
en: 'Hello',
es: 'Hola',
}
return <span> {trans[locale.code]} </span>
}
useAuth
用于获取当前登录用户的信息以及与之交互的方法。它返回一个包含以下属性的对象:
Property | 描述 |
---|---|
user | 当前登录的用户 |
logOut | 登出当前用户的方法 |
refreshCookie | 触发静默刷新用户认证令牌的方法 |
setToken | 设置用户的令牌,用于解码并重置内存中的用户和令牌 |
token | 登录用户的令牌(可用于创建预览链接等) |
refreshPermissions | 重新加载权限(当影响权限的内容发生变化时很有用) |
permissions | 当前用户的权限 |
'use client'
import { useAuth } from '@payloadcms/ui'
import type { User } from '../payload-types.ts'
const Greeting: React.FC = () => {
// highlight-start
const { user } = useAuth<User>()
// highlight-end
return <span>Hi, {user.email}!</span>
}
useConfig
用于获取 Payload 的 客户端配置。
'use client'
import { useConfig } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const { config } = useConfig()
// highlight-end
return <span>{config.serverURL}</span>
}
如果需要通过 slug 获取特定的 collection 或 global 配置,getEntityConfig
是最有效的方式:
'use client'
import { useConfig } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const { getEntityConfig } = useConfig()
const mediaConfig = getEntityConfig({ collectionSlug: 'media' })
// highlight-end
return (
<span>媒体集合包含 {mediaConfig.fields.length} 个字段。</span>
)
}
useEditDepth
返回当前组件所处的编辑层级"深度"。在模态窗口中添加新文档/编辑文档等场景下,编辑深度非常有用。
'use client'
import { useEditDepth } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const editDepth = useEditDepth()
// highlight-end
return <span>我的组件位于第 {editDepth} 层</span>
}
usePreferences
返回设置和获取用户偏好的方法。更多信息请参阅此处。
useTheme
返回当前选定的主题(light
、dark
或 auto
),一个用于更新主题的设置函数,以及一个布尔值 autoMode
,用于确定是否应根据用户设备偏好自动设置主题值。
'use client'
import { useTheme } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const { autoMode, setTheme, theme } = useTheme()
// highlight-end
return (
<>
<span>
当前主题是 {theme},autoMode 为 {autoMode}
</span>
<button
type="button"
onClick={() =>
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'))
}
>
切换主题
</button>
</>
)
}
useTableColumns
返回用于操作表格列的属性和方法:
属性/方法 | 描述 |
---|---|
columns | 列的当前状态,包括它们的激活状态和配置 |
LinkedCellOverride | 用于覆盖表格中链接单元格的组件 |
moveColumn | 用于重新排序列的方法。接受 { fromIndex: number, toIndex: number } 作为参数 |
resetColumnsState | 将列重置回 collection 配置中定义的默认配置的方法 |
setActiveColumns | 设置特定列为激活状态同时保留现有列顺序的方法。接受要激活的列名数组 |
toggleColumn | 切换单个列可见性的方法。接受列名字符串作为参数 |
'use client'
import { useTableColumns } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// highlight-start
const { setActiveColumns, resetColumnsState } = useTableColumns()
const activateSpecificColumns = () => {
// 仅激活 id 和 createdAt 列
// 其他列保持当前的激活/非激活状态
// 原始列顺序会被保留
setActiveColumns(['id', 'createdAt'])
}
const resetToDefaults = () => {
// 重置为 collection 配置中定义的默认列
resetColumnsState()
}
// highlight-end
return (
<div>
<button type="button" onClick={activateSpecificColumns}>
激活特定列
</button>
<button type="button" onClick={resetToDefaults}>
重置为默认
</button>
</div>
)
}
useDocumentEvents
useDocumentEvents
钩子提供了一种订阅跨文档事件的方式,例如对抽屉内嵌套文档的更新。该钩子会报告当前编辑文档范围之外的文档事件。该钩子提供以下功能:
属性 | 描述 |
---|---|
mostRecentUpdate | 包含最近更新文档的对象,包含 entitySlug 、id (如果是 collection)和 updatedAt 属性 |
reportUpdate | 用于报告文档更新的方法,接受与 mostRecentUpdate 属性相同的参数。 |
示例:
'use client'
import { useDocumentEvents } from '@payloadcms/ui'
const ListenForUpdates: React.FC = () => {
const { mostRecentUpdate } = useDocumentEvents()
return <span>{JSON.stringify(mostRecentUpdate)}</span>
}
目前 useDocumentEvents
钩子仅跟踪最近更新的文档,
但未来会根据需要跟踪更多文档相关事件,例如
文档创建、删除等。
useStepNav
useStepNav
钩子提供了一种更改应用头部步骤导航面包屑链接的方式。
属性 | 描述 |
---|---|
setStepNav | 一个状态设置函数,用于设置 stepNav 数组。 |
stepNav | 一个 StepNavItem 数组,其中每个 StepNavItem 都有一个标签和可选的 url。 |
示例:
'use client'
import { type StepNavItem, useStepNav } from '@payloadcms/ui'
import { useEffect } from 'react'
export const MySetStepNavComponent: React.FC<{
nav: StepNavItem[]
}> = ({ nav }) => {
const { setStepNav } = useStepNav()
useEffect(() => {
setStepNav(nav)
}, [setStepNav, nav])
return null
}
usePayloadAPI
usePayloadAPI
钩子是一个实用工具,用于向你的 Payload 实例发起 REST API 请求并响应式地处理返回结果。它允许你获取数据并与数据交互,同时当参数变化时自动更新。
该钩子返回一个包含两个元素的数组:
- 一个包含 API 响应的对象。
- 一组用于修改请求参数的方法。
示例:
'use client'
import { usePayloadAPI } from '@payloadcms/ui'
const MyComponent: React.FC = () => {
// 使用 ID 从 collection 中获取数据项
const [{ data, isError, isLoading }, { setParams }] = usePayloadAPI(
'/api/posts/123',
{
initialParams: { depth: 1 },
},
)
if (isLoading) return <p>加载中...</p>
if (isError) return <p>获取数据时发生错误。</p>
return (
<div>
<h1>{data?.title}</h1>
<button onClick={() => setParams({ cacheBust: Date.now() })}>
刷新数据
</button>
</div>
)
}
参数说明:
Property | Description |
---|---|
url | 获取数据的 API 端点。相对路径会自动添加 Payload API 路由前缀。 |
options | 包含初始请求参数和初始状态配置的对象。 |
options
参数接受以下属性:
Property | Description |
---|---|
initialData | 使用此数据替代初始请求。如果未提供,则会立即执行请求。 |
initialParams | 定义请求使用的初始参数。默认为空对象 {} 。 |
返回值说明:
返回数组的第一项是一个包含以下属性的对象:
Property | Description |
---|---|
data | API 响应数据。 |
isError | 布尔值,表示请求是否失败。 |
isLoading | 布尔值,表示请求是否正在进行中。 |
返回数组的第二项是一个包含以下方法的对象:
Property | Description |
---|---|
setParams | 更新请求参数,必要时会触发重新获取数据。 |
更新数据
setParams
函数可用于更新请求参数并触发重新获取数据:
setParams({ depth: 2 })
这在需要触发另一次数据获取(无论 url
参数是否改变)的场景中非常有用。
useRouteTransition
路由过渡(Route transitions)能在页面间导航时为用户提供即时视觉反馈。这在慢速网络环境下导航到数据密集或处理复杂的页面时特别有用。
默认情况下,来自 @payloadcms/ui
的任何 Link
实例都会自动触发路由过渡。
import { Link } from '@payloadcms/ui'
const MyComponent = () => {
return <Link href="/somewhere">前往某处</Link>
}
你也可以通过编程方式触发路由过渡,例如在使用 next/router
的 router.push
时。要实现这一点,可以使用 useRouteTransition
钩子提供的 startRouteTransition
方法包裹你的函数调用。
'use client'
import React, { useCallback } from 'react'
import { useTransition } from '@payloadcms/ui'
import { useRouter } from 'next/navigation'
const MyComponent: React.FC = () => {
const router = useRouter()
const { startRouteTransition } = useRouteTransition()
const redirectSomewhere = useCallback(() => {
startRouteTransition(() => router.push('/somewhere'))
}, [startRouteTransition, router])
// ...
}