1

I'm trying to get a handle on the new tRPC version 10 with a basic shopping list CRUD Nextjs app. I have successfully set up the tRPC endpoint with "get all" and a "create" handlers and can confirm that they both work after testing from the front end. However, I can't seem to update my state with the data from the "get all" call. In older tRPC versions we would have updated the state as follows:

const data = trpc.useQuery(["items.getAll"], {
  onSuccess(items) {
    setItems(items);
  },
});

In version 10 however, they've done away with the useQuery() arguments in favour of conditional status returns according to docs. I tried updating the state as follows:

const [items, setItems] = useState<ShoppingItem[]>([]);

const data = trpc.shoppingItem.getAll.useQuery();

if (data.isSuccess) {
  setItems(data.data);
}

This understandably causes a "Too many re-renders" error since each time the state updates it re-renders the component, therefore triggering a new isSuccess and re-updating the state.

What is the proper way to update state from tRPCv10?

My full component follows for context:

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 = trpc.shoppingItem.getAll.useQuery();

  if (data.isSuccess) {
    setItems(data.data);
  }

  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={"hello"} />
      )}

      <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;

2 Answers2

0

I had the same problem and managed to get it to work by adding 'undefined' as an argument to useQuery().

 const { data: itemsData, isLoading } = trpc.items.getAllItems.useQuery(undefined, {
    onSuccess(items) {
      setItems(items)
    }
  })

Not sure if this is the correct way to update state in tRPC v10 but it seems to be working. Let me know if you find the correct way to do it!

  • Thanks for your suggestion, I didn't think of trying this way although I did find another way to do it. I also don't know if my way is correct since the docs don't really describe state updates: ``` const [items, setItems] = useState([]); const [modalOpen, setModalOpen] = useState(false); const { data: shoppingItems } = trpc.shoppingItem.getAll.useQuery(); useEffect(() => { shoppingItems && setItems(shoppingItems); }, [shoppingItems]); return (...) ``` – Caelan Curry Oct 21 '22 at 11:31
0

I'm not sure if this is the intended way to deal with this but I managed to get it working as follows:

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

  const { data: shoppingItems } = trpc.shoppingItem.getAll.useQuery();

  useEffect(() => {
    shoppingItems && setItems(shoppingItems);
  }, [shoppingItems]);

I call useEffect to update the state when there is a change in the data pulled from the tRPC call.

Additionally, I pass down the setItems() method to my form component and call that on the success of the mutation:

  const [input, setInput] = useState<string>("");

  const addItem = trpc.shoppingItem.addItem.useMutation({
    async onSuccess(newItem) {
      setItems((prev) => [...prev, newItem]);
      console.log(newItem);
    },
  });

  const handleAddItem = () => {
    addItem.mutate({ name: input });
    setModalOpen(false);
  };