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
相关产品推荐
相关产品推荐

