React严格模式下useState存储宝可梦类型出现重复的解决方法
解决React严格模式下宝可梦类型数组重复的问题
问题根源
React严格模式在开发环境中会重复执行useEffect等生命周期逻辑,目的是帮你排查潜在的代码问题。你的代码里每次useEffect执行都会触发18次接口请求,每次请求完成后都往状态数组里追加数据,两次执行就导致了36条重复数据。
最优解决方案:一次性获取所有类型
PokeAPI本身提供了获取全量类型列表的接口,无需循环调用单个类型接口,一次请求就能拿到所有数据,从根源避免重复问题:
function App() { const [types, setTypes] = useState([]); useEffect(() => { const fetchAllTypes = async () => { try { const res = await axios.get('https://pokeapi.co/api/v2/type/'); // 从返回结果中提取所有类型名称 const typeNames = res.data.results.map(item => item.name); setTypes(typeNames); } catch (error) { console.log(error); } }; fetchAllTypes(); }, []); return ( // ...你的组件内容 ); }
这个方案不仅解决了重复问题,还把18次请求压缩为1次,性能更优。
若坚持循环单个请求的解决办法
如果一定要保留循环调用单个类型接口的逻辑,可以通过以下两种方式避免重复:
方式1:用useRef标记是否已完成请求
用useRef存储一个布尔值,标记是否已经发起过请求,第二次useEffect执行时直接跳过:
function App() { const [types, setTypes] = useState([]); const hasFetched = useRef(false); // 标记是否已发起过请求 useEffect(() => { if (hasFetched.current) return; // 已请求过则直接返回 hasFetched.current = true; const fetchType = async (typeId) => { try { const res = await axios.get(`https://pokeapi.co/api/v2/type/${typeId}`); setTypes(prev => [...prev, res.data.name]); } catch (error) { console.log(error); } }; for (let i = 1; i <= 18; i++) { fetchType(i); } }, []); return ( // ...你的组件内容 ); }
方式2:并行请求后一次性更新状态
用Promise.all等待所有请求完成后再统一设置状态,同时通过判断状态数组是否已有数据,避免重复更新:
function App() { const [types, setTypes] = useState([]); useEffect(() => { if (types.length > 0) return; // 已有数据则跳过 const fetchAllTypes = async () => { try { // 创建所有请求的Promise数组 const requests = Array.from({ length: 18 }, (_, idx) => axios.get(`https://pokeapi.co/api/v2/type/${idx + 1}`) ); // 等待所有请求完成 const responses = await Promise.all(requests); // 提取所有类型名称 const typeNames = responses.map(res => res.data.name); setTypes(typeNames); } catch (error) { console.log(error); } }; fetchAllTypes(); }, [types]); return ( // ...你的组件内容 ); }
注意:这种方式在开发环境下还是会触发两次共36个请求,只是第二次会因为状态已有数据而跳过更新,因此更推荐用一次性请求的方案。
内容的提问来源于stack exchange,提问作者Keitaro
相关产品推荐
相关产品推荐

