You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React点击独立导航链接时,如何阻止非目标数据发生变更?

问题解决:React路由点击链接时非目标数据意外更新

问题原因

  1. 数组原地修改的副作用:Array.sort()是原地排序方法,会直接修改原数组。当App组件中任意状态(posts或albums)更新时,Posts和Album组件都会重新渲染,此时各自的排序操作会修改原始数据数组,导致另一个组件的展示数据同步变化。
  2. 状态更新逻辑错误: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 12:26:16