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

React Router v6使用useParams获取id时触发TypeError错误求助

问题:React函数组件中出现TypeError: Cannot read properties of undefined (reading 'paletteName')

我正在学习Colt Steele的React课程,因为课程过时,改用函数组件。现在遇到以下错误:

colorHelpers.js:7  Uncaught TypeError: Cannot read properties of undefined (reading 'paletteName')
    at generatePalette (colorHelpers.js:7:1)
    at PaletteWrapper (App.js:17:1)
    at renderWithHooks (react-dom.development.js:16305:1)
    at mountIndeterminateComponent (react-dom.development.js:20074:1)
    at beginWork (react-dom.development.js:21587:1)
    at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1)
    at invokeGuardedCallback (react-dom.development.js:4277:1)
    at beginWork$1 (react-dom.development.js:27451:1)
    at performUnitOfWork (react-dom.development.js:26557:1)

相关代码

App.js

import { Routes, Route, useParams } from "react-router-dom";

import { generatePalette } from "./colorHelpers";
import Palette from "./Palette";
import seedColors from "./seedColors";

function App() {
  const findPalette = (id) => {
    const palette = seedColors.find((palette) => palette.id === id);
    console.log(palette);
    return palette;
  };

  const PaletteWrapper = () => {
    const { id } = useParams();

    const palette = generatePalette(findPalette(id));

    return <Palette palette={palette} />;
  };

  return (
    <div className="App">
      <Routes>
        <Route path="/" element={<h1>Welcome Home</h1>} />
        <Route path="/palette/:id" element={<PaletteWrapper />} />
      </Routes>

      {/* <Palette palette={generatePalette(seedColors[3])} /> */}
    </div>
  );
}

export default App;

Palette.js

import { useState } from "react";
import ColorBox from "./ColorBox";
import { Navbar } from "./Navbar";
import "./Palette.css";

const Palette = (props) => {
  const [level, setLevel] = useState(500);
  const [format, setFormat] = useState("hex");

  const changeFormat = (format) => {
    setFormat(format);
  };

  const changeLevel = (newLevel) => {
    setLevel(newLevel);
  };

  const { colors, paletteName, emoji } = props.palette;

  const colorBoxes = colors[level].map((color) => (
    <ColorBox key={color.name} background={color[format]} name={color.name} />
  ));

  return (
    <div className="Palette">
      <Navbar
        level={level}
        changeLevel={changeLevel}
        changeFormat={changeFormat}
        format={format}
      />
      <div className="Palette-colors">{colorBoxes}</div>
      <div className="Palette-footer">
        {paletteName}
        <span className="emoji">{emoji}</span>
      </div>
    </div>
  );
};

export default Palette;

colorHelpers.js

import chroma from "chroma-js";

const levels = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];

export function generatePalette(starterPalette) {
  let newPalette = {
    paletteName: starterPalette.paletteName,
    id: starterPalette.id,
    emoji: starterPalette.emoji,
    colors: {},
  };

  for (let level of levels) {
    newPalette.colors[level] = [];
  }

  for (let color of starterPalette.colors) {
    let scale = getScale(color.color, 10).reverse();
    for (let i in scale) {
      newPalette.colors[levels[i]].push({
        name: `${color.name} ${levels[i]}`,
        id: color.name.toLowerCase().replace(/ /g, "-"),
        hex: scale[i],
        rgb: chroma(scale[i]).css(),
        rgba: chroma(scale[i])
          .css()
          .replace("rgb", "rgba")
          .replace(")", ",1.0)"),
      });
    }
  }
  return newPalette;
}

function getRange(hexColor) {
  const end = "#fff";
  return [chroma(hexColor).darken(1.4).hex(), hexColor, end];
}

function getScale(hexColor, numOfColors) {
  return chroma.scale(getRange(hexColor)).mode("lab").colors(numOfColors);
}

seedColors.js

export default [
  {
    paletteName: "Material UI Colors",
    id: "material-ui-colors",
    emoji: "🎨",
    colors: [
      { name: "red", color: "#F44336" },
      { name: "pink", color: "#E91E63" },
      { name: "purple", color: "#9C27B0" },
      { name: "deeppurple", color: "#673AB7" },
      { name: "indigo", color: "#3F51B5" },
      { name: "blue", color: "#2196F3" },
      { name: "lightblue", color: "#03A9F4" },
      { name: "cyan", color: "#00BCD4" },
      { name: "teal", color: "#009688" },
      { name: "green", color: "#4CAF50" },
      { name: "lightgreen", color: "#8BC34A" },
      { name: "lime", color: "#CDDC39" },
      { name: "yellow", color: "#FFEB3B" },
      { name: "amber", color: "#FFC107" },
      { name: "orange", color: "#FF9800" },
      { name: "deeporange", color: "#FF5722" },
      { name: "brown", color: "#795548" },
      { name: "grey", color: "#9E9E9E" },
      { name: "bluegrey", color: "#607D8B" },
    ],
  },
  ...
];

我已经排查了findPalette、generatePalette函数以及Palette组件,但仍无法解决该错误,希望得到帮助。


解决方案

1. 核心原因

错误发生在generatePalette第7行,说明传入的starterPalette是undefined,也就是findPalette(id)没找到匹配的调色板。可能的场景:

  • URL中的id和seedColors里的id不匹配(拼写错误、大小写不一致)
  • 首次渲染时useParams()还未获取到id值

2. 修复步骤

步骤1:给findPalette添加兜底处理

修改findPalette函数,当找不到匹配项时返回默认调色板,避免传入undefined到generatePalette:

const findPalette = (id) => {
  const palette = seedColors.find((palette) => palette.id === id);
  // 兜底:返回第一个调色板作为默认值
  return palette || seedColors[0];
};

步骤2:在PaletteWrapper中做空值判断

调用generatePalette前先检查结果是否存在,避免传递无效值:

const PaletteWrapper = () => {
  const { id } = useParams();
  const foundPalette = findPalette(id);
  
  // 未找到时显示提示
  if (!foundPalette) {
    return <h1>Palette not found!</h1>;
  }

  const palette = generatePalette(foundPalette);
  return <Palette palette={palette} />;
};

步骤3:校验URL参数与seedColors的匹配性

确认访问的URL(如/palette/material-ui-colors)中的id和seedColors里的id完全一致,注意大小写和连字符格式。

步骤4:给Palette组件添加props兜底

防止props.palette为空导致解构错误:

const Palette = (props) => {
  // 解构时添加默认值
  const { colors = {}, paletteName = "Unknown Palette", emoji = "❓" } = props.palette || {};
  
  // ... 其他代码
};

3. 额外优化:用可选链避免深层访问错误

在generatePalette中添加可选链操作,即使starterPalette为空也不会直接报错:

export function generatePalette(starterPalette) {
  let newPalette = {
    paletteName: starterPalette?.paletteName || "Default Palette",
    id: starterPalette?.id || "default-id",
    emoji: starterPalette?.emoji || "🎨",
    colors: {},
  };
  
  // ... 其他代码
}

内容的提问来源于stack exchange,提问作者Alesia Haxhi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 10:40:11