TypeScript如何更实用地优化React菜单渲染代码
TypeScript 餐饮菜单组件务实优化方案
针对你不熟悉TS、想写实用可维护代码的需求,以下优化全部围绕「少搞花架子、降低出错概率、好改好维护」的原则,重点优化RenderMenu组件,同时覆盖Menu组件的改进点。
第一步:先写最基础的类型定义(TS最实用的核心用法)
不用一开始就学复杂的泛型、工具类型,TS入门最有用的就是把你用到的数据结构明确定义出来,编辑器会自动给你做代码提示,传错字段、写错属性名直接飘红,不用等跑起来才发现bug。
如果是本地导入的JSON文件TS本来会自动推导类型,但显式写一遍的好处是你自己对数据结构心里有数,后面换成接口请求数据也能直接复用:
// 单个餐品的结构,和DinnerData里的字段一一对应 interface MenuItem { name: string; price: string | number; // 兼容数字价格、带货币符号的字符串价格两种情况 text: string; image?: string; // 可选字段:后面如果数据里带餐品图可以直接用 } // 菜单分类的结构 interface MenuCategory { name: string; // 分类名,比如Starters前菜 items: MenuItem[]; } // 组件可接收的参数,没用的参数不用写,后面要加功能直接扩 interface MenuProps { defaultImage?: string; menuTitle?: string; }
重点:RenderMenu 组件优化
先列现有代码的几个实际问题:
- key属性位置错误:外层循环的最外层元素没加key,反而把key写在了内部的h4标签上,React会一直报key警告;内层餐品循环完全没加key
- 多余的空Fragment嵌套:内层map返回的空标签没有任何作用,只会增加代码嵌套层级
- 硬编码的图片地址写在循环内部,后面要换图得翻循环逻辑找
- 没有兜底逻辑:如果数据为空、某个分类下没有餐品,会直接渲染空结构甚至报错
- 用index作为key存在隐患:如果后面要调整分类顺序、增删分类/餐品,index作为key会导致组件渲染错乱
小提示:
<></>是React Fragment的简写形式,但是简写不能传key属性,循环里需要给Fragment加key时,必须显式导入Fragment后使用<Fragment key={xxx}>的写法。
优化后代码:
import { Fragment } from 'react'; import { data } from './DinnerData'; import { Dinner } from './Dinner'; export const RenderMenu = ({ // 把硬编码的默认图提为组件参数,要改直接改默认值就行 defaultImage = "https://i.imgur.com/kbpceNv.jpg" }: MenuProps) => { // 数据兜底:如果菜单数据为空,直接返回不渲染,避免报错 if (!data?.types?.length) return null; return ( <> {data.types.map((category) => { // 分类下没有餐品直接跳过,不渲染空区块 if (!category.items?.length) return null; return ( // 循环的key必须加在最外层返回的元素上,用分类名当key比index靠谱 <Fragment key={category.name}> <div className="row"> <div className="col-xl-12"> <div className="section-title text-center"> <h4>{category.name}</h4> </div> </div> </div> <div className="row menu_style1"> {category.items.map((item) => { return ( <Dinner // 餐品key拼上分类名,避免不同分类下有同名餐品导致key重复 key={`${category.name}-${item.name}`} title={item.name} price={item.price} image={item.image || defaultImage} // 支持餐品自定义图,没传就用默认图 > {/* children不用显式传,直接写在标签中间和HTML习惯一致,可读性更好 */} {item.text} </Dinner> ); })} </div> </Fragment> ); })} </> ); };
Menu 组件优化
现有代码的可改进点:
- 存在无用的空p标签,直接删掉即可
- 菜单标题、区块id硬编码,复用性差
- 外层多余的空Fragment嵌套,会增加无意义的节点层级
- 原来的区块id写的
home和菜单区块语义不符,锚点跳转容易出错
优化后代码:
export const Menu = ({ menuTitle = "Vår meny", defaultImage }: MenuProps) => { return ( <section className="about-area pt-60 m-2 p-2" id="menu-section"> <div className="container mb-5"> <div className="row"> <div className="col-xl-12 mb-60"> <div className="section-title text-center"> <h1>{menuTitle}</h1> </div> </div> </div> {/* 把参数透传给子组件,不用在子组件里写死配置 */} <RenderMenu defaultImage={defaultImage} /> </div> </section> ); };
几个适合新手的TS/React务实编码习惯
- 不要滥用Fragment:只有需要返回多个平级元素、又不想加额外div容器的时候才用,单个根元素直接返回即可
- key优先用业务唯一字段(比如分类名、餐品ID、餐品名),除非你确定列表永远不会排序、增删,否则不要全用index当key
- 所有外部数据(导入的JSON、接口返回值)都用可选链
?.做兜底判断,避免数据结构异常导致整个页面白屏 - 类型定义按需写,用到啥字段补啥字段,不用一开始就追求覆盖所有边缘情况,够用就行
- 硬编码的常量(默认图、默认标题、固定文案)提到组件参数或组件外部的常量区,后面修改不用翻JSX逻辑找
内容的提问来源于stack exchange,提问作者JamesG
相关产品推荐
相关产品推荐

