React项目中uuid生成ID后useParams匹配详情数据失效问题解决
问题描述
我基于API搭建了一个新闻项目,可通过分类切换获取对应数据。点击新闻卡片进入详情页时,因API返回数据无ID字段,我使用uuid生成ID,期望通过useParams获取ID并匹配对应详情数据,但该功能无法正常工作,请问如何解决?
相关组件代码
NewsDetail组件
import React, { useState, useEffect } from 'react' import { useParams } from 'react-router-dom' const NewsDetail = ({ state }) => { const { id } = useParams(); return ( <div> { state .filter((a) => a.id === id) .map((card, index) => ( <> <div className="card" key={index}> <h2>{card.title}</h2> <h2>{card.content}</h2> <img src={card.imageUrl} alt="" /> </div> </> )) } </div> ) } export default NewsDetail
NewsItem组件
import React from 'react' import clock from "../components/assets/img/Clock.svg" import user from "../components/assets/img/User.svg" import { Link } from 'react-router-dom' const NewsItem = (props) => { const { imageUrl, title, author, content, date, id } = props return ( <Link className="col-lg-4 p-2" to={`/detail/${id}`}> <div className="newsItem"> <img src={imageUrl} alt='newsPhoto' /> <h2>{id}</h2> <div className="itemBody"> <p className='title'>{title}</p> <div className="line"></div> <p className='content'>{content}</p> <div className="itemfooter"> <span><img src={clock} alt='clock' />{date}</span> <span><img src={user} alt='user' />{author}</span> </div> </div> </div> </Link> ) } export default NewsItem
Home组件
import React, { useEffect, useState } from "react"; import NewsItem from "./NewsItem"; import SpinnerLoad from "./SpinnerLoad"; import { v4 as uuidv4 } from 'uuid'; const Home = (props) => { const Category = [ "all", "business", "sports", "world", "technology", "entertainment", "science" ]; const { state, setState} = props; const [loading, setLoading] = useState(false) const fetchValue = (category) => { fetch(`https://inshorts-api.herokuapp.com/news?category=${category}`) .then(res => res.json()) .then(res => { setState(res.data) setLoading(true) }) .catch((error) => console.log(error)) console.log(state); setLoading(false); }; // const fetchValue = async () => { // try { // const data = await axios // .get(`https://inshorts-api.herokuapp.com/news?category=sports`) // .then(res => { // console.log(res); // setState(res.data) // }) // setLoading(true) // console.log(loading); // } catch (e) { // console.log(e); // } // } const CategoryButton = ({ category }) => ( <button onClick={() => fetchValue(category)} style={{ textTransform: 'capitalize' }}>{category}</button> ); useEffect(() => { fetchValue('all') },[]) return ( <> <div className="header-bg"> <h1 className="mb-3">News</h1> <div className="btns "> {Category.map((value, index) => { return <CategoryButton category={value} key={index} />; })} </div> </div> <div className="news"> <div className="container"> <div className="row"> { !loading ? <SpinnerLoad/> : state.map((data,index) => { return ( <NewsItem imageUrl={data.imageUrl} author={data.author} title={data.title} content={data.content} date={data.date} id={uuidv4()} key={index} /> ); }) } </div> </div> </div> </> ); }; export default Home;
解决方案
核心问题是:你在Home组件里给NewsItem临时生成的uuid并没有存入到state的新闻数据中,导致详情页无法通过id匹配到对应数据。按以下步骤修改:
1. 在获取API数据时给每条新闻添加uuid
修改Home组件的fetchValue方法,在保存数据到state前,给每条新闻添加唯一id,同时修正loading状态的逻辑错误:
const fetchValue = (category) => { setLoading(true); // 请求开始时标记加载中 fetch(`https://inshorts-api.herokuapp.com/news?category=${category}`) .then(res => res.json()) .then(res => { // 给每条新闻添加uuid字段 const newsWithId = res.data.map(item => ({ ...item, id: uuidv4() })); setState(newsWithId); }) .catch(error => console.log(error)) .finally(() => { setLoading(false); // 请求完成后关闭加载状态 }); };
2. 修改Home组件中NewsItem的传参
现在state里的每条新闻都自带id了,直接传入数据中的id即可,同时用id作为key(比index更稳定):
state.map((data) => { return ( <NewsItem imageUrl={data.imageUrl} author={data.author} title={data.title} content={data.content} date={data.date} id={data.id} // 使用数据自带的id key={data.id} // 用唯一id作为key /> ); })
3. 优化NewsDetail组件(可选)
用find代替filter+map,因为我们只需要匹配单条数据,同时处理新闻不存在的情况:
import React from 'react' import { useParams } from 'react-router-dom' const NewsDetail = ({ state }) => { const { id } = useParams(); const news = state.find(a => a.id === id); if (!news) { return <div>该新闻不存在</div>; } return ( <div className="card"> <h2>{news.title}</h2> <h2>{news.content}</h2> <img src={news.imageUrl} alt={news.title} /> </div> ) } export default NewsDetail
内容的提问来源于stack exchange,提问作者user18134288
相关产品推荐
相关产品推荐

