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钩子接受以下参数:

PropertyDescription
path如果不提供path,则会使用name。这是字段在表单数据中的路径。
validate在表单提交到服务器_之前_执行的客户端验证函数。与字段级验证不同,后者仅在服务器端运行。
disableFormData如果为true,提交表单时该字段不会包含在表单数据中。
hasRows如果为true,该字段将被视为包含行的字段。这对arrayblocks等字段类型很有用。

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 钩子允许你控制父级可折叠组件:

PropertyDescription
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 中显示的数据。
defaultLimitList View 中默认显示的条目数量限制。
defaultSortList 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 会返回完整的语言环境对象,包含 labelrtl(从右到左)属性以及 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

返回当前选定的主题(lightdarkauto),一个用于更新主题的设置函数,以及一个布尔值 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包含最近更新文档的对象,包含 entitySlugid(如果是 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 请求并响应式地处理返回结果。它允许你获取数据并与数据交互,同时当参数变化时自动更新。

该钩子返回一个包含两个元素的数组:

  1. 一个包含 API 响应的对象。
  2. 一组用于修改请求参数的方法。

示例:

'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>
  )
}

参数说明:

PropertyDescription
url获取数据的 API 端点。相对路径会自动添加 Payload API 路由前缀。
options包含初始请求参数和初始状态配置的对象。

options 参数接受以下属性:

PropertyDescription
initialData使用此数据替代初始请求。如果未提供,则会立即执行请求。
initialParams定义请求使用的初始参数。默认为空对象 {}

返回值说明:

返回数组的第一项是一个包含以下属性的对象:

PropertyDescription
dataAPI 响应数据。
isError布尔值,表示请求是否失败。
isLoading布尔值,表示请求是否正在进行中。

返回数组的第二项是一个包含以下方法的对象:

PropertyDescription
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/routerrouter.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])

  // ...
}