React Query对接双API时点击同义词更新单词报错排查
问题根因
- 核心报错来自Synonyms组件的事件参数冲突:你写的
onClick={(item) => {setword(item)}}中,形参item实际是DOM点击事件对象,不是遍历到的同义词字符串。把事件对象作为查询词传给API,接口收到的是无效参数,直接触发请求错误。 - Word组件中用组件内普通变量
synarray配合push存同义词属于React反模式:每次组件重渲染synarray都会被重置为空数组,在渲染流程中调用getsynonym往数组里写值会导致状态不一致、渲染异常。 - 错误分支的JSX里直接写
{setword("run")}会在渲染阶段直接触发状态更新,引发无限重渲染循环。
修复方案
- 修复Synonyms组件点击事件的参数冲突,不要和map遍历的词条变量重名,点击时传入遍历到的同义词字符串
- 用
useMemo基于接口返回数据计算派生的同义词、释义内容,不要在渲染过程中手动修改普通数组 - 把错误场景下重置单词的逻辑放到
useEffect里执行,禁止在JSX渲染阶段直接调用状态更新函数 - 读取接口嵌套属性时加可选链校验,避免接口返回结构不符合预期时触发读属性报错
修复后代码
Synonyms.jsx
import React from "react"; const Synonyms = ({ synonymarray, setword }) => { return ( <ul> {synonymarray?.map((wordItem, i) => ( <li key={i} style={{ cursor: "pointer" }} onClick={() => { setword(String(wordItem)); }} > {wordItem} </li> ))} </ul> ); }; export default Synonyms;
Word.jsx
import { useState, useMemo, useEffect } from "react"; // 其余第三方组件、方法导入保持原有内容不变 const Word = ({ fetchWord, fetchsynonym }) => { const [word, setword] = useState("run"); const { data, isLoading, isFetching, isError } = useQuery( ["definition", word], () => fetchWord(word) ); const { data: Sdata, isLoading: Sloading, isFetching: Sfetching, isError: Serror, } = useQuery(["synonym", word], () => fetchsynonym(word)); // 计算同义词数组 const synarray = useMemo(() => { if (Sloading || Sfetching || Serror || !Sdata?.noun?.syn) { return []; } return Sdata.noun.syn.slice(0, 3).filter(Boolean); }, [Sdata, Sloading, Sfetching, Serror]); // 计算释义内容 const definition = useMemo(() => { if (isLoading || isFetching) return ""; if (isError || !data?.[0]?.meanings?.[0]?.definitions?.[0]?.definition) return ""; return data[0].meanings[0].definitions[0].definition; }, [data, isLoading, isFetching, isError]); const handleTranscript = (transcript) => { if (!transcript || transcript.length === 0) return; setword(String(transcript).trim()); }; // 错误场景下重置默认词 useEffect(() => { if (isError) { setTimeout(() => setword("run"), 2000); } }, [isError]); return ( <VStack w={"100%"}> {isError ? ( <> <Heading>{word}</Heading> <Voice handleTranscript={handleTranscript} setword={setword} word={word} /> <p>请求出错,请重试,仅支持查询单个英文单词</p> </> ) : ( <> <Heading mt={"10%"} textAlign={"center"}> hi Welcome to WordUp dont know how to spell a word and want to know what it means ? Click below. </Heading> <Flex padding={2} boxShadow={"0 0 1rem 0 rgba(0, 0, 0, .2)"} borderRadius={10} flexDir={"column"} w={"50%"} h={[700, 500, 300]} alignItems={"center"} justifyContent={"space-around"} > <Heading> Word: {word}</Heading> <Heading padding={10} alignSelf={"center"}> {isLoading || isFetching ? "加载中..." : definition} </Heading> {Sloading || Sfetching ? <p>同义词加载中...</p> : <Synonyms synonymarray={synarray} setword={setword} /> } <Voice handleTranscript={handleTranscript} setword={setword} word={word} /> </Flex> </> )} </VStack> ); }; export default Word;
补充说明
之前点击同义词后触发接口报错,本质是事件对象被当成查询词传给了API:React Query会把查询key里的内容做序列化,事件对象序列化后是"[object Object]"字符串,API识别不了这个无效单词直接返回错误。
所有从接口数据、状态派生出来的内容(比如这里的同义词列表、释义文本),不要用普通变量手动维护,用useMemo做计算即可,避免数据不同步。
内容的提问来源于stack exchange,提问作者flowoverstack
相关产品推荐
相关产品推荐

