如何使用React Hooks和useParams实现点击卡片跳转对应详情页功能
具体实现指导
先修复现有代码的潜在bug
EventList.js中posts的初始值为空字符串,应该改为空数组,避免后续列表判断逻辑异常:
// 替换原来的useState定义 const [posts, setPosts] = useState([]);
步骤1:调整路由与跳转逻辑
(可选优化)避免标题特殊字符导致路由失效
标题可能包含空格、特殊符号,跳转时建议对标题做编码处理,修改Event.js中的Link组件:
<Link to={`/${encodeURIComponent(post.title)}`}> <button className={styles.btns}>查看详情</button> </Link>
如果你的post数据有唯一id,更推荐用id作为路由参数,匹配准确性更高:
- Event.js跳转改为:
to={/event/${post.id}} - App.js对应路由改为:
<Route path="/event/:postId" exact children={<EventDetails />}></Route>
步骤2:修改EventDetails.js用useParams实现数据匹配
以下是完整的修改后代码,通过useParams获取路由参数,拉取数据后匹配对应帖子详情:
import React, { useState, useEffect } from "react"; import Navbar from "../Navbar/Navbar"; import styles from "./EventDetails.module.css"; import "bootstrap/dist/css/bootstrap.min.css"; import { Link, useParams } from "react-router-dom"; import axios from "axios"; // 替换为你自己的接口配置 let config = { Authorization: "3055f8f90fa44bbe8bda05385a20690a" }; const baseurl = "https://newsapi.org/v2/top-headlines?sources=bbc-news"; const EventDetails = () => { // 从路由中获取参数,注意参数名要和App.js路由中定义的一致 const { title } = useParams(); const [currentPost, setCurrentPost] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { // 解码路由中的标题参数 const decodedTitle = decodeURIComponent(title); // 拉取新闻列表匹配对应帖子 axios .get(baseurl, { headers: config }) .then((response) => { const allPosts = response.data.articles; // 匹配标题一致的帖子 const target = allPosts.find(post => post.title === decodedTitle); setCurrentPost(target); }) .catch((error) => console.error(`请求错误: ${error}`)) .finally(() => setLoading(false)); }, [title]); // 异常状态处理 if (loading) return <div style={{padding: '20px', textAlign: 'center'}}>加载中...</div>; if (!currentPost) return <div style={{padding: '20px', textAlign: 'center'}}>未找到对应帖子</div>; return ( <div> <Navbar /> <div className={styles.eventBg}> <div className="container"> <div> <img src={currentPost.urlToImage} alt={currentPost.title} width="100%" className={styles.heroEventImage} /> </div> <div className={styles.bookingDetails}> <div className={styles.nameBook}> <div> <div className={styles.eventNameHeader}> <h1 className={styles.eventName}>{currentPost.title}</h1> </div> <div className={styles.genre}> <div className={styles.genreText}>作者:{currentPost.author}</div> </div> <div style={{margin: '20px 0'}}> {currentPost.content} </div> </div> <div className={styles.bookingBtn}> <div className={styles.booking}> <Link to="/GeneralBooking"> <button className={styles.bookBtn} style={{ height: "60px", fontSize: "18px" }} > Book </button> </Link> </div> </div> </div> <div className={styles.venueTime}> <div className={styles.dateTime}> <div className={styles.dateTimeText}>发布时间:{new Date(currentPost.publishedAt).toLocaleDateString()}</div> <div className={styles.price}>来源:{currentPost.source.name}</div> </div> </div> </div> </div> </div> </div> ); }; export default EventDetails;
可选优化:减少重复请求
每次进入详情页都拉取全量列表会浪费请求资源,可以在列表页拉取数据时缓存到本地,详情页优先读缓存:
- 在EventList.js的AllPosts方法中添加缓存逻辑:
.then((response) => { const allPosts = response.data.articles; setPosts(allPosts); // 新增缓存到本地存储 localStorage.setItem('postList', JSON.stringify(allPosts)); })
- 在EventDetails.js的useEffect中优先读缓存:
useEffect(() => { const decodedTitle = decodeURIComponent(title); // 先读本地缓存 const localList = localStorage.getItem('postList'); if (localList) { const allPosts = JSON.parse(localList); const target = allPosts.find(post => post.title === decodedTitle); setCurrentPost(target); setLoading(false); return; } // 缓存不存在再发请求 axios.get(baseurl, { headers: config }) // 原有请求逻辑不变 }, [title])
内容的提问来源于stack exchange,提问作者sud
相关产品推荐
相关产品推荐

