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

React数组排序后渲染旧状态的问题求助

React组件排序后始终渲染数组旧状态的解决方案

问题描述

开发React组件时,通过.map遍历state中的数组渲染子组件,同时实现了Popover组件用.sort方法按指定参数排序。但切换排序规则时,页面始终显示数组的旧状态——比如先按likes排序,再切换为dislikes排序,页面仍保留第一次排序后的结果。

问题原因

  1. 数组sort方法原地修改:Array.sort()会直接修改原数组,而React依赖state的引用变化触发重渲染。原地修改后数组引用未变,React判定state未更新,因此不触发重渲染。
  2. useEffect依赖逻辑混乱:Popover组件的useEffect依赖了已排序的jokes数组,每次排序修改原数组后会重复触发useEffect,导致逻辑循环或状态判断错误。
  3. key使用不稳定值:.map渲染时用uuidv4()生成动态key,会导致子组件每次渲染都被卸载重建,既影响性能,也会干扰React的状态更新判断。

解决方案

1. 避免原地修改数组,生成新数组后更新state

排序前先通过扩展运算符([...arr])或slice()复制原数组,再对新数组排序,确保state引用变化触发重渲染。

2. 分离原始数据与排序后数据

将从接口获取的原始数据单独存在一个state中,排序时基于原始数据操作,避免污染源数据,同时让useEffect依赖更清晰。

3. 使用稳定的key值

用数组项的唯一标识(如每个joke的id)作为.map的key,不要使用动态生成的uuid。

修改后的代码示例

Piadas组件(主组件)

export default function Piadas() {
  const [searchBarContent, setSearchBarContent] = useState("");
  const [order, setOrder] = useState("");
  const [originalJokes, setOriginalJokes] = useState([]); // 存储接口返回的原始数据
  const [jokes, setJokes] = useState([]); // 存储排序/筛选后的数据

  useEffect(() => {
    if (originalJokes.length === 0) {
      fetchData("getJokes");
    }
  }, [originalJokes]);

  function fetchData(url) {
    fetch(`http://localhost:3006/${url}`)
      .then(response => response.json())
      .then(data => {
        setOriginalJokes(data.data);
        setJokes(data.data); // 初始化显示原始数据
      });
  }

  return (
    <div>
      <Head>
        <title>Só piada boa</title>
        <meta
          name="description"
          content="Aplicação utilizando Banco de dados, CRUD, REST e Next.JS"
        />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <main>
        <ContainerStageBackgroundPiadas>
          <LightBrownMobileWrapper>
            <UtilsWrapper
              searchBarContent={searchBarContent}
              setSearchBarContent={setSearchBarContent}
              order={order}
              setOrder={setOrder}
              originalJokes={originalJokes}
              setJokes={setJokes}
            />
            <CardWrapper>
              {jokes.map((data) => (
                <Card data={data} key={data.id} /> {/* 使用数据自身的唯一id作为key */}
              ))}
            </CardWrapper>
          </LightBrownMobileWrapper>
        </ContainerStageBackgroundPiadas>
      </main>
    </div>
  );
}

Popover组件(排序组件)

export default function Popover({ order, setOrder, originalJokes, setJokes }) {
  useEffect(() => {
    if (!order || originalJokes.length === 0) return;
    
    // 复制原始数组,避免原地修改
    const sortedJokes = [...originalJokes].sort((a, b) => {
      if (order === "likes") {
        return a.likes < b.likes ? 1 : -1; // 降序排列
      } else if (order === "dislikes") {
        return a.dislikes < b.dislikes ? 1 : -1; // 降序排列
      }
      return 0;
    });
    setJokes(sortedJokes);
  }, [order, originalJokes, setJokes]);

  return (
    <PopoverContainer style={{ width: "100%" }}>
      <PopoverTrigger>Ordenar</PopoverTrigger>
      <PopoverContent>
        <div>
          <input
            type="radio"
            name="orderRadio"
            value="likes"
            onChange={(event) => setOrder(event.target.value)}
            checked={order === "likes"} // 同步选中状态
          />
          <p>Likes</p>
        </div>
        <div>
          <input
            type="radio"
            name="orderRadio"
            value="dislikes"
            onChange={(event) => setOrder(event.target.value)}
            checked={order === "dislikes"} // 同步选中状态
          />
          <p>Deslikes</p>
        </div>
      </PopoverContent>
    </PopoverContainer>
  );
}

内容的提问来源于stack exchange,提问作者Lucas Fernandes Tolotto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 15:45:46