ARTICLE DETAIL

资讯详情

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

Storybook Addon 中读写 Story Args 实战:useArgs Hook 在 manager-api 下的完整解析

Storybook Addon 中读写 Story Args 实战:useArgs Hook 在 manager-api 下的完整解析 Storybook Addon 中读写 Story Args 实战useArgs Hook 在 manager-api 下的完整解析【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook本文以 Storybook 仓库中的官方代码片段 args-usage-with-addons.md 为主体系统讲解如何在**自定义 Addon管理器端**中通过useArgs读取当前 Story 的args、增量更新或批量重置 args并结合仓库内 manager-api 与 preview-api 的源码实现拆解其背后“管理器 → 预览 iframe”的事件同步链路与使用边界。读完本文你将掌握在 Addon 面板、工具栏组件中操作 story args 的准确姿势并能在装饰器preview 端与 Addonmanager 端之间做出正确的 API 选择。为什么 Addon 需要直接操作 args在 Storybook 中args是 Story 的“输入参数”等同于 React 的 props、Angular 的 inputs/outputs。修改 args 会让当前 Story 以新参数重新渲染见 README-store.md 中 Args 一节。这一机制除了支撑内置的 Controls 面板外也是大量第三方 Addon 的核心能力来源——例如工具类 Addon 希望一键切换某个参数并驱动 Story 重渲染时就必须能读取并写入当前 Story 的 args。因此 Storybook 在官方 hooks 中提供了useArgs在管理器manager——即 Addon 面板、工具条组件运行的环境——从storybook/manager-api导入在预览preview——即装饰器、Story 渲染函数运行的环境——从storybook/preview-api导入。本仓库的代码片段 args-usage-with-addons.md 演示的正是前一种场景也是本文的核心骨架import { useArgs } from storybook/manager-api; const [args, updateArgs, resetArgs] useArgs(); // To update one or more args: updateArgs({ key: value }); // To reset one (or more) args: resetArgs((argNames: [key])); // To reset all args resetArgs();该片段同时被两处官方文档引用说明其典型的落点场景addons-api.mdx 的 “Storybook hooks → useArgs” 小节把它作为 manager 端 hooks 家族的一员介绍args.mdx 的 “Using args in addons”告诉正在编写 Addon 的开发者用 manager 端useArgs读写 story args。从 manager 端使用 useArgs参数签名与行为细节返回值四元组含 initialArgsmanager 端的useArgs定义于 code/core/src/manager-api/root.tsx#L496-L513实际返回的是一个长度为 4 的元组export function useArgs(): [Args, (newArgs: Args) void, (argNames?: string[]) void, Args] { const { getCurrentStoryData, updateStoryArgs, resetStoryArgs } useStorybookApi(); const data getCurrentStoryData(); const args data?.type story ? data.args : {}; const initialArgs data?.type story ? data.initialArgs : {}; const updateArgs useCallback( (newArgs: Args) updateStoryArgs(data as API_StoryEntry, newArgs), [data, updateStoryArgs] ); const resetArgs useCallback( (argNames?: string[]) resetStoryArgs(data as API_StoryEntry, argNames), [data, resetStoryArgs] ); return [args!, updateArgs, resetArgs, initialArgs!]; }四个返回值的作用如下返回值类型含义argsArgs当前 Story 的实时 args若当前条目不是 story例如 docs 页面则为空对象{}updateArgs(newArgs: Args) void传入部分args 进行增量更新未涉及的 arg 保持不变resetArgs(argNames?: string[]) void传入 arg 名数组时仅将这几个 arg 重置回initialArgs不传参则重置当前 Story 的全部 argsinitialArgsArgs当前 Story 在 CSF 中声明的初始 argsreset 的“基准值”来源关键实现事实args与initialArgs均来自useStorybookApi().getCurrentStoryData()并只在其type story时取值否则回退为空对象——因此在非 story 上下文中调用updateArgs不会产生有效更新见 root.tsx。两个 setter 均以useCallback包装并依赖data会随当前 Story 切换自动重建不必担心闭包捕获过期的 story id。注意官方文档resetArgs的完整形态是resetArgs([key])其中argNames?: string[]是可选参数——不传即全量重置片段中的写法resetArgs((argNames: [key]))属于示意性笔误实际调用时应传数组字面量resetArgs([key])。与代码片段的对应关系把片段翻译成完整行为即为const [args, updateArgs, resetArgs, initialArgs] useArgs(); // 1) 读取args 可直接使用例如 args.someProp console.log(args); // 2) 增量更新只改其中的 key其余 args 保持不变Story 立即以新参数重渲染 updateArgs({ key: value }); // 3) 局部重置把 key 重置回 CSF 里声明的 initialArgs resetArgs([key]); // 4) 全量重置恢复该 Story 声明的全部初始参数 resetArgs();在真实 Addon 中组装useArgs只能在 Addon 的管理器组件如 panel、tool 类型内使用。下面是一个把读写闭环起来的 toolbar 风格组件示例可置于你的 addon 源码的manager模块中import { useArgs } from storybook/manager-api; export const ToggleDensityTool () { const [args, updateArgs, resetArgs] useArgs(); // 从 args 读取当前值 const compact args.compact; return ( button onClick{() compact ? resetArgs([compact]) : updateArgs({ compact: true }) } {compact ? Reset density : Enable compact density} /button ); };若要了解 addon 如何被注册进 manager、以及 panel/tool 等不同类型的编写范式可参考 addons-api.mdx 的 hooks 综述 与官方相关 snippets如 storybook-addons-api-useaddonstate.md、storybook-addon-tool-initial-setup.md。事件同步原理manager 如何驱动 preview 重渲染manager 与 preview 运行在两个不同的 JavaScript 环境manager UI 与渲染 iframe中useArgs的魔法实际是一条跨 iframe 的事件通道。第一步manager 端派发更新事件updateArgs与resetArgs最终调用的是 manager-api stories 模块中的updateStoryArgs/resetStoryArgs见 code/core/src/manager-api/modules/stories.ts#L756-L771updateStoryArgs: (story, updatedArgs) { const { id: storyId, refId } story; provider.channel?.emit(UPDATE_STORY_ARGS, { storyId, updatedArgs, options: { target: refId }, }); }, resetStoryArgs: (story, argNames) { const { id: storyId, refId } story; provider.channel?.emit(RESET_STORY_ARGS, { storyId, argNames, options: { target: refId }, }); },其中UPDATE_STORY_ARGS与RESET_STORY_ARGS是预定义事件名。事件载荷携带storyId、更新内容并通过options: { target: refId }指定消息送往的目标 frame——当 Story 来自组合进来的远程 ref如 composeStorybook 场景时事件会被路由到正确的 ref 而不是本地 preview。这一按 frame 路由行为有对应的单元测试覆盖见 code/core/src/manager-api/tests/stories.test.ts。第二步preview 端接收并应用preview 侧的Preview类在初始化时即订阅这两个事件code/core/src/preview-api/modules/preview-web/Preview.tsx#L147-L149channel.on(UPDATE_STORY_ARGS, onUpdateArgs)、channel.on(RESET_STORY_ARGS, onResetArgs)。收到事件后preview 会把新的 args 写入当前 story 的 store从而触发一次以新 args 进行的重渲染。大量交互式测试覆盖了从事件发出到渲染更新的完整链路例如 PreviewWeb.test.ts。从源码结构可以推断这正是在 manager 面板里改 args → 画布里的 Story 立即刷新这一体验的底层实现。preview 端的 useArgs同一签名另一套环境同样的 hook 在 preview 端storybook/preview-api也存在一份独立实现位于 code/core/src/preview-api/modules/addons/hooks.ts#L614-L633export function useArgsTArgs extends Args Args(): [ TArgs, (newArgs: PartialTArgs) void, (argNames?: (keyof TArgs)[]) void, ] { const channel addons.getChannel(); const { id: storyId, args } useStoryContextRenderer, TArgs(); const updateArgs useCallback( (updatedArgs: PartialTArgs) channel.emit(UPDATE_STORY_ARGS, { storyId, updatedArgs }), [channel, storyId] ); const resetArgs useCallback( (argNames?: (keyof TArgs)[]) channel.emit(RESET_STORY_ARGS, { storyId, argNames }), [channel, storyId] ); return [args as TArgs, updateArgs, resetArgs]; }与 manager 端实现相比值得注意的差异返回三元组preview 端只返回[args, updateArgs, resetArgs]没有initialArgs支持泛型可用useArgs{ name: string; age: number }()获得带类型的args与PartialTArgs约束的updateArgs获取方式不同它直接从useStoryContext()读取当前 story 的id与args并通过 channel向 manager 发送UPDATE_STORY_ARGS/RESET_STORY_ARGS——与 manager 端构成事件流中对称的另一半。其行为由 code/core/src/preview-api/modules/store/hooks.test.ts#L542-L569 中的单元测试验证断言emit被以正确的事件名与载荷调用。preview 端的典型应用场景是在装饰器或 story 内响应交互后改写 args例如把点击/切换事件映射为参数变化官方 snippet 可见page-story-args-within-story.md在 Page 类 story 内部通过useArgs将子组件回调与 args 同步decorator-with-updateArgs.md在 decorator 中用updateArgs包装事件处理。若你在 story 渲染函数内使用 Storybook hooks包括useArgs切勿混用 React 自带的useState/useEffect/useRef二者的重渲染与副作用不经过同一 hooks 上下文容易在重渲染时报错——这一约束在 args.mdx 中作为 warning 明确给出。使用边界与工程建议args 必须可序列化且只放“渲染所需值”根据 README-store.md 的说明args 的值会通过事件通道在 preview 与 manager 之间同步也可能被写入 URL因此必须是可序列化的不能包含函数/回调args 会被直接透传给 story 渲染因此应只存放 story 渲染真正需要的值如需携带更复杂的信息请放到parameters或 addon 自有状态如useAddonState中。性能减少无谓的重渲染addons-api.mdx 的 hooks 综述 在介绍 manager hooksuseArgs、useGlobals、useStorybookState等时统一建议优先用React.memo、useMemo、useCallback优化组件避免因 args / globals / 内部 state 高频变化引发大范围重渲染。在 Addon 面板中应尽量只从args中解构本 addon 关心的键并使用updateArgs做局部增量更新而非每次都重建整份 args。全局参数场景请改用 useGlobals如果希望设置能跨 Story 保持如主题、语言等全局偏好应使用面向 globals 的useGlobalshook其 manager 实现同样在 root.tsx而不是useArgs。相关用法见 storybook-addons-api-useglobal.md 与 addon-consume-and-update-globaltype.md。reset 的语义resetArgs()的重置目标是该 Story 的initialArgs即 CSF 中声明的初始值而非“清除参数”。部分重置传入的argNames数组只影响列出的键。若需要在 manager 端拿到initialArgs作为比对或“恢复按钮是否可点”的依据直接使用 manager 版useArgs解构出的第 4 个返回值即可。小结Addonmanager 端读写当前 Story 的 args使用storybook/manager-api的useArgs()其返回[args, updateArgs, resetArgs, initialArgs]updateArgs支持部分更新、resetArgs支持按名局部或全量重置依据initialArgs。装饰器 / story 内部使用storybook/preview-api的useArgsT()返回三元组并支持泛型两者分别处于同一条UPDATE_STORY_ARGS/RESET_STORY_ARGS事件链路的两端见 manager stories.ts 与 preview Preview.tsx。args 必须可序列化、只存放渲染所需值跨 Story 保持的设置请改用useGlobalsAddon 组件应配合React.memo/useMemo/useCallback控制重渲染成本。若需深入了解相关 API 全貌建议继续阅读 addons-api.mdx 中useChannel、useAddonState、useParameter、useGlobals等 manager hooks并结合 args.mdx 中关于 args、argTypes 与 Controls 的完整说明按需取用。【免费下载链接】storybookStorybook is the industry standard workshop for building, documenting, and testing UI components in isolation项目地址: https://gitcode.com/GitHub_Trending/st/storybook创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表