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

React点击按钮无需State重新渲染列表,解决setState异步滞后问题

解决点击「Mark Read」按钮后实时更新items状态的问题

核心问题分析

你当前的代码直接修改items变量不会触发React组件重渲染——React仅在状态(useState/useReducer)或props变化时才会重新渲染。之前用useState觉得状态更新滞后,是因为没正确使用函数式状态更新,也没配合TRPC mutation的回调来同步状态。

正确实现方案

1. 将处理后的items存入useState

把原本的普通变量items改成useState,确保状态变化能触发重渲染:

import { useState, useEffect } from 'react';

const Main: React.FC<MainProps> = (props) => {
  // Get All Feeds
  const feeds = trpc.feed.getAllFeeds.useQuery();

  // Get Items
  let unsorteditems;
  if (props.FeedType == "one") {
    unsorteditems = trpc.feed.getItemsFromFeed.useQuery({ feedId: props.feedId });
  } else if (props.FeedType == "readalready") {
    unsorteditems = trpc.feed.getReadItems.useQuery();
  } else {
    unsorteditems = trpc.feed.getFeedItemsFromAllFeeds.useQuery();
  }

  // 用useState存储处理后的items
  const [processedItems, setProcessedItems] = useState<typeof items>(undefined);

  // 当查询数据变化时,重新处理并更新状态
  useEffect(() => {
    if (!unsorteditems.data || !feeds.data) return;

    // 先拷贝数组再排序,避免修改原始查询数据
    const sorted = [...unsorteditems.data].sort(
      (objA, objB) => Number(objB.createdAt) - Number(objA.createdAt)
    );

    const newItems = sorted.map((item) => {
      const feed = feeds.data.find((feed) => feed.id === item.feedId);
      return {
        ...item,
        feedTitle: feed?.title,
        feedLogoUrl: feed?.LogoUrl,
        markedAsRead: false,
      };
    });

    setProcessedItems(newItems);
  }, [unsorteditems.data, feeds.data]);

  // ...其余代码
}

2. 优化Mark Read的点击逻辑

使用函数式setState确保基于最新状态修改,同时在TRPC mutation成功后同步后端数据:

// Mark Read function
const markreadmutation = trpc.feed.markReadUsingUpsert.useMutation({
  onSuccess: () => {
    // 失效对应查询的缓存,让TRPC自动拉取最新数据(可选,保证前后端数据一致)
    switch (props.FeedType) {
      case "one":
        trpc.feed.getItemsFromFeed.invalidate({ feedId: props.feedId });
        break;
      case "readalready":
        trpc.feed.getReadItems.invalidate();
        break;
      default:
        trpc.feed.getFeedItemsFromAllFeeds.invalidate();
    }
  },
});

const handleMarkRead = (itemId: string, index: number) => {
  markreadmutation.mutate({ itemId });
  
  // 立即更新本地状态,实现UI即时反馈
  setProcessedItems(prevItems => {
    if (!prevItems) return prevItems;
    return prevItems.map((item, i) => 
      i === index ? { ...item, markedAsRead: true } : item
    );
  });
};

3. 修改渲染逻辑

将渲染时的items替换为processedItems:

return (
  <>
    <div className="basis-5/12 border-l border-slate-300/40">
      {/* 省略标题部分代码 */}
      <div>
        {processedItems?.length === 0 && <p>No Items</p>}
        {processedItems?.map((item, i) => (
          <div
            className={`border-slate-300/4 relative flex h-28 flex-col border-b ${item.markedAsRead ? "bg-gray-200" : "bg-white"}`}
            key={item.id}
          >
            <button onClick={() => handleMarkRead(item.id, i)}>
              [Mark Read]
            </button>
          </div>
        ))}
      </div>
    </div>
  </>
);

额外优化建议

  • 不要直接修改原始查询数据(比如unsorteditems.data?.sort(...)),sort会改变原数组,应先拷贝再排序:[...unsorteditems.data].sort(...)。
  • 可给查询添加enabled选项,确保依赖数据存在时再发起请求,避免无效请求。

内容的提问来源于stack exchange,提问作者Michael Kremenetsky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 07:01:02