You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>
        );
      })}
    </>
  );
};

现有代码的可改进点:

  • 存在无用的空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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 08:15:27