React点击独立导航链接时,如何阻止非目标数据发生变更?
问题解决:React路由点击链接时非目标数据意外更新
问题原因
- 数组原地修改的副作用:
Array.sort()是原地排序方法,会直接修改原数组。当App组件中任意状态(posts或albums)更新时,Posts和Album组件都会重新渲染,此时各自的排序操作会修改原始数据数组,导致另一个组件的展示数据同步变化。 - 状态更新逻辑错误:Navbar中的
setPosts和setAlbums把原数组嵌套进新数组([...shuffledPosts, shuffledPosts]),导致数据结构混乱,同时每次更新都会触发组件重新渲染,进一步放大了排序的副作用。
修复步骤
1. 避免原地修改数组,复制后再排序
修改Posts.js和Album.js中的排序逻辑,先复制原数组再进行排序,防止修改原始状态数据:
Posts.js 修改后
import React from 'react'; export default function Posts({ posts }) { // 先复制原数组,再排序,避免修改原数据 const shuffledPosts = [...posts].sort(() => 0.5 - Math.random()).slice(0, 3); return ( <> <h4>以下是posts数据:</h4> {shuffledPosts.map((i) => ( <div key={i.id}> {i.id}: <h3>{i.title}</h3> <p>{i.body}</p> </div> ))} </> ); }
Album.js 修改后
import React from 'react'; export default function Album({ albums }) { // 复制原数组后再排序 const shuffledAlbums = [...albums].sort(() => 0.5 - Math.random()).slice(0, 3); return ( <> <div> <h4>以下是album数据</h4> {shuffledAlbums.map((i) => ( <label key={i.id}> <i> {`${i.id}: ${i.title}`} <br /> </i> </label> ))} </div> </> ); }
2. 修正Navbar中的状态更新逻辑
点击链接时,基于原始数据重新打乱目标状态,而非修改原数据结构。我们在App组件中单独保存原始数据,确保每次打乱都基于初始数据源:
App.js 修改后
import React, { useState, useEffect } from 'react'; import './style.css'; import Album from './pages/Album'; import Posts from './pages/Posts'; import Navbar from './components/Navbar'; import { DataContext } from './context/DataContext.js'; export default function App() { // 保存原始API数据,避免被修改 const [originalPosts, setOriginalPosts] = useState([]); const [originalAlbums, setOriginalAlbums] = useState([]); // 用于展示的打乱后的数据 const [posts, setPosts] = useState([]); const [albums, setAlbums] = useState([]); // 初始化获取API数据 useEffect(() => { fetch('https://jsonplaceholder.typicode.com/posts') .then(res => res.json()) .then(data => { setOriginalPosts(data); // 初始打乱一次 setPosts([...data].sort(() => 0.5 - Math.random()).slice(0, 3)); }); fetch('https://jsonplaceholder.typicode.com/albums') .then(res => res.json()) .then(data => { setOriginalAlbums(data); setAlbums([...data].sort(() => 0.5 - Math.random()).slice(0, 3)); }); }, []); // 单独定义打乱posts的方法 const shufflePosts = () => { setPosts([...originalPosts].sort(() => 0.5 - Math.random()).slice(0, 3)); }; // 单独定义打乱albums的方法 const shuffleAlbums = () => { setAlbums([...originalAlbums].sort(() => 0.5 - Math.random()).slice(0, 3)); }; return ( <DataContext.Provider value={{ posts, setPosts, albums, setAlbums }}> <div> <Navbar shufflePosts={shufflePosts} shuffleAlbums={shuffleAlbums} /> <Posts posts={posts} /> <Album albums={albums} /> </div> </DataContext.Provider> ); }
Navbar.js 修改后
import React from 'react'; import { Link } from 'react-router-dom'; export default function Navbar({ shufflePosts, shuffleAlbums }) { return ( <> <Link to="#link1" onClick={(e) => { e.preventDefault(); // 阻止锚点默认跳转行为 shufflePosts(); }} > postlink </Link> <br /> <Link to="#link2" onClick={(e) => { e.preventDefault(); shuffleAlbums(); }} > albumlink </Link> <br /> <Link to="#link3">commentlink</Link> </> ); }
3. 关键说明
- 原始数据隔离:将API获取的初始数据单独存储,避免排序操作污染原始数据源。
- 独立状态更新:点击对应链接时仅更新目标数据状态,非目标数据状态保持不变,对应组件不会重新渲染,也就不会触发排序操作。
- 阻止默认行为:使用
e.preventDefault()避免Link的锚点跳转带来的不必要页面滚动。
内容的提问来源于stack exchange,提问作者user
相关产品推荐
相关产品推荐

