如何在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
相关产品推荐
相关产品推荐

