如何在StoryBook与React TypeScript中创建动态Material UI Icon原子组件?
解决你的动态Icon组件需求
我来帮你搞定这两个问题,咱们一步一步来:
问题分析
先拆解下你当前实现的核心问题:
- 无法动态切换图标:你现在是直接传入已经渲染好的图标元素(比如
<AccessTimeIcon />),这种方式相当于把图标实例固定死了,Storybook没法动态替换不同的图标组件。 - 样式props不生效:你的
MyIcon组件只是把传入的materialIcon原封不动返回,没有把fontSize、color这些props传递给MUI的图标组件,所以样式完全没变化。
解决方案
1. 重构Icon组件,支持传入图标组件并传递props
我们需要把materialIcon的类型从React.ReactElement改成React.ComponentType,这样就能接收图标组件本身,然后在MyIcon内部渲染这个组件时,把所有相关的props传递过去。
修改后的Icon.tsx:
import React from "react"; import type { IconProps } from "@mui/material/Icon"; // 修改props类型,materialIcon接收组件类型而非已渲染元素 interface MyIconProps extends IconProps { materialIcon: React.ComponentType<IconProps>; } const MyIcon = ({ materialIcon: MaterialIcon, ...props }: MyIconProps) => { // 渲染传入的图标组件,并传递所有props(fontSize、color等) return <MaterialIcon {...props} />; }; export default MyIcon;
2. 更新Storybook配置,支持动态切换图标
现在我们可以在Story的argTypes里添加materialIcon的控制项,让它可以选择不同的图标组件,同时修改stories的args,传入组件本身而非实例。
修改后的Icon.stories.tsx:
import React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; import MyIcon from "./Icon"; import AccessTimeIcon from "@mui/icons-material/AccessTime"; import PersonOutlineIcon from "@mui/icons-material/PersonOutline"; import HomeIcon from "@mui/icons-material/Home"; // 可以加更多图标示例 export default { title: "Atoms/MyIcon", component: MyIcon, argTypes: { color: { options: ["primary", "secondary", "warning", "error", "info", "success", "action"], control: { type: "radio" }, }, fontSize: { options: ["small", "medium", "large"], control: { type: "radio" }, }, // 添加materialIcon的控制项,支持选择不同图标 materialIcon: { options: { AccessTime: AccessTimeIcon, PersonOutline: PersonOutlineIcon, Home: HomeIcon, }, control: { type: "select" }, }, }, } as ComponentMeta<typeof MyIcon>; const Template: ComponentStory<typeof MyIcon> = (args) => <MyIcon {...args} />; export const Default = Template.bind({}); Default.args = { fontSize: "medium", color: "primary", materialIcon: AccessTimeIcon, // 传入组件本身,而非<AccessTimeIcon /> }; // 如果你还需要保留特定的story示例,也可以这样写 export const Person = Template.bind({}); Person.args = { fontSize: "large", color: "warning", materialIcon: PersonOutlineIcon, };
效果说明
- 现在你可以在Storybook的controls面板里,通过下拉菜单切换不同的图标组件,完全实现动态效果。
fontSize、color等props会被自动传递给MUI的图标组件,样式会根据设置实时变化。- 这种方式也更灵活,后续添加新图标只需要在
argTypes的materialIcon.options里新增即可。
内容的提问来源于stack exchange,提问作者VIVEK JAIN
相关产品推荐
相关产品推荐

