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

如何在tRPCv10调用成功后正确更新React状态?

tRPC v10 中避免无限重渲染的状态更新方案

你的问题出在直接在组件渲染流程中调用setItems:每次data.isSuccess为true时,执行setItems(data.data)会触发组件重渲染,重渲染后又会再次进入这个判断逻辑,循环往复导致无限重渲染。

tRPC v10 里推荐两种正确的处理方式:

方案一:直接使用tRPC返回的查询结果(推荐)

tRPC的useQuery已经内部维护了查询状态,完全不需要额外声明本地useState来存储数据,直接使用data.data即可,还能自然处理加载、错误状态:

修改后的组件代码:

import { useState } from "react";
import { ShoppingItem } from "@prisma/client";
import type { NextPage } from "next";
import Head from "next/head";
import ItemModal from "../components/ItemModal";
import { trpc } from "../utils/trpc";

const Home: NextPage = () => {
  const [modalOpen, setModalOpen] = useState<boolean>(false);

  // 直接使用tRPC的查询结果
  const { data: items, isLoading, error } = trpc.shoppingItem.getAll.useQuery();

  // 处理加载/错误状态(可选但推荐)
  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>加载失败:{error.message}</div>;

  return (
    <>
      <Head>
        <title>Shopping List</title>
        <meta name="description" content="Generated by create-t3-app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      {modalOpen && (
        // 修正setItems的传值,传递状态更新函数
        <ItemModal setModalOpen={setModalOpen} setItems={setItems} />
      )}

      <main className="mx-auto my-12 max-w-3xl">
        <div className="flex justify-between">
          <h2 className="text-2xl font-semibold">My Shopping List</h2>
          <button
            className="rounded-md bg-violet-500 p-2 text-sm text-white transition hover:bg-violet-600"
            type="button"
            onClick={() => setModalOpen(true)}
          >
            Add Item
          </button>
        </div>

        <ul className="mt-4">
          {/* 直接用tRPC返回的items,可选链避免初始undefined */}
          {items?.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      </main>
    </>
  );
};

export default Home;

方案二:用useEffect监听查询结果更新本地状态(仅当需要本地状态时使用)

如果因为业务需求必须维护本地独立状态(比如需要对查询结果做本地修改后再展示),可以用useEffect监听data.data和data.isSuccess的变化,仅在数据成功返回且发生变化时更新本地状态:

import { useState, useEffect } from "react";
import { ShoppingItem } from "@prisma/client";
import type { NextPage } from "next";
import Head from "next/head";
import ItemModal from "../components/ItemModal";
import { trpc } from "../utils/trpc";

const Home: NextPage = () => {
  const [items, setItems] = useState<ShoppingItem[]>([]);
  const [modalOpen, setModalOpen] = useState<boolean>(false);

  const { data, isSuccess, isLoading, error } = trpc.shoppingItem.getAll.useQuery();

  // 用useEffect监听,避免无限重渲染
  useEffect(() => {
    if (isSuccess && data) {
      setItems(data);
    }
  }, [isSuccess, data]); // 依赖项仅在状态变化时触发

  if (isLoading) return <div>加载中...</div>;
  if (error) return <div>加载失败:{error.message}</div>;

  return (
    <>
      <Head>
        <title>Shopping List</title>
        <meta name="description" content="Generated by create-t3-app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      {modalOpen && (
        <ItemModal setModalOpen={setModalOpen} setItems={setItems} />
      )}

      <main className="mx-auto my-12 max-w-3xl">
        <div className="flex justify-between">
          <h2 className="text-2xl font-semibold">My Shopping List</h2>
          <button
            className="rounded-md bg-violet-500 p-2 text-sm text-white transition hover:bg-violet-600"
            type="button"
            onClick={() => setModalOpen(true)}
          >
            Add Item
          </button>
        </div>

        <ul className="mt-4">
          {items.map((item) => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      </main>
    </>
  );
};

export default Home;

另外注意你代码中ItemModal的setItems传值错误,应该传递setItems函数而不是字符串"hello",上面的代码已经修正了这个问题。

内容的提问来源于stack exchange,提问作者Caelan Curry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 09:05:23