0

I'm trying to learn how typescript, react and tRPC behaves together, I've this page questions.tsx:

import { NextPage } from 'next';
import { useRouter } from 'next/router';
import { QuestionCard } from '~/components/questionCard';
import { api } from '~/utils/api';

const QuestionFlow: NextPage = () => {
  const router = useRouter();

  const { subjectId, chapterId } = router.query;

  const subId = typeof subjectId === 'string' ? parseInt(subjectId) : 0;
  const chapId = typeof chapterId === 'string' ? parseInt(chapterId) : 0;

  //Get All questions from Chapter
  const { data: allChapterQuestions } = api.chapterQuestions.getAllChapterQuestionsFromSubjectChapter.useQuery({ subjectId: subId, chapterId: chapId });

  if (allChapterQuestions && allChapterQuestions.length > 0) {
    const { data: questionInfo } = api.chapterQuestions.getById.useQuery({ chapterQuestionId: allChapterQuestions[0]!.id });
    const { data: questionOptions } = api.questionOptions.getAllOptionsFromQuestionId.useQuery({ questionId: allChapterQuestions[0]!.id });

    // Check if both questionInfo and questionOptions are available before rendering the QuestionCard
    const isDataAvailable = questionInfo && questionOptions;

    return (
      <>
        <p>Question Flow</p>
        <p>subjectId: {subId}</p>
        <p>chapterId: {chapId}</p>
        {isDataAvailable ?
          <QuestionCard questionInfo={questionInfo} questionOptions={questionOptions} /> :
          <p>ERROR WITH SOME DATA</p>
        }
      </>
    );
  } else {
    return (
      <>
        <p>Question Flow</p>
        <p>subjectId: {subId}</p>
        <p>chapterId: {chapId}</p>
        <p>ERROR WITH THE DATA</p>
      </>
    );
  }
};

export default QuestionFlow;

As you may guess this renders first the "else" return, to then throw the error:

Unhandled Runtime Error
Error: Rendered more hooks than during the previous render.

I understand that this is caused to call my api. inside a conditional that will change on the next re-frame if the conditional change.

I know that one work around will be to put all those 3 api calls inside just one that retrieves the info at once, BUT:

There are a lot of cases that I won't be able to do that, and I don't know how to work around it when the moment arrives, there are a lot of cases that I want to check the response from the api and make another api call depending on that.

How should I tackle this issue?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Lotan
  • 4,078
  • 1
  • 12
  • 30
  • `useQuery` is a react hook thus can only be called at component top level, never in nested if-else branch. You can extract your if branch into a new component, this way the hook calls are un-nested and appear at top level again. – hackape Jul 24 '23 at 22:46

1 Answers1

0

As it was recommended in the comment, you cannot use hook with conditions, it's a general React rule, not trpc.

What you can do here is roughly this:

  1. Move hooks out of the if-else block
  2. Add enabled option to the hooks
  3. Only keep rendering logic under your condition
const hasChapters = allChapterQuestions && allChapterQuestions.length > 0

const { data: questionInfo } = api
      .chapterQuestions
      .getById
      // add `enabled` option
      .useQuery({ chapterQuestionId: allChapterQuestions[0]!.id }, { enabled: hasChapters});

// and now you can have your if clause here:
if (questionInfo) {
  // ...
} else {
  // ...
}
Danila
  • 15,606
  • 2
  • 35
  • 67