Skip to content

Commit

Permalink
queryOptions 리팩토링 (#29)
Browse files Browse the repository at this point in the history
* refactor: quiz 관련 쿼리 레이어 정리

* refactor: 새로운 quiz 쿼리 사용

* remove: 기존의 quiz 훅 제거

* chore: import 관련 빌드 문제 해결

* style: mutate 이름 submitQuiz 로 변경
  • Loading branch information
bbearcookie authored Dec 22, 2023
1 parent 8875172 commit 5fa7d31
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 86 deletions.
25 changes: 13 additions & 12 deletions app/quizzes/[id]/choice-form.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
'use client';

import React, { useState } from 'react';
import { useGetChoicesOfQuiz, postQuizSubmission } from '@/hooks/quiz';
import { useMutation } from '@tanstack/react-query';
import { useGetChoicesOfQuiz, useSubmitQuiz } from '@/services/quiz/hooks';
import { useRouter } from 'next/navigation';
import Button from '@/components/common/buttons/button';
import LoadingSpinner from '@/components/common/loading-spinner/loading-spinner';

export default function ChoiceForm({ quizId }: { quizId: number }) {
const { data: choices } = useGetChoicesOfQuiz(quizId);
const [errorMessage, setErrorMessage] = useState('');
const router = useRouter();

const { mutate, isPending, isSuccess } = useMutation({
mutationFn: postQuizSubmission,
onSuccess: () => router.push(`/quizzes/${quizId}/answer`),
onError: (error) => setErrorMessage(error.message),
});
const { data: choices } = useGetChoicesOfQuiz(quizId);
const { mutate: submitQuiz, isPending, isSuccess } = useSubmitQuiz();

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
Expand All @@ -32,10 +27,16 @@ export default function ChoiceForm({ quizId }: { quizId: number }) {
return;
}

mutate({
quizId,
choiceId: choice.id,
});
submitQuiz(
{
quizId,
choiceId: choice.id,
},
{
onSuccess: () => router.push(`/quizzes/${quizId}/answer`),
onError: (error) => setErrorMessage(error.message),
}
);
};

return (
Expand Down
12 changes: 3 additions & 9 deletions app/quizzes/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,21 @@ import {
QueryClient,
dehydrate,
} from '@tanstack/react-query';
import { getQuiz, getChoicesOfQuiz } from '@/hooks/quiz';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { dracula } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import Link from 'next/link';
import ChoiceForm from './choice-form';
import quizOptions from '@/services/quiz/options';

export default async function Page({ params }: { params: { id: string } }) {
const queryClient = new QueryClient();
const quizId = Number(params.id) ?? 0;

const [quiz] = await Promise.all([
queryClient.fetchQuery({
queryKey: ['quiz', quizId],
queryFn: () => getQuiz(quizId),
}),
queryClient.fetchQuery({
queryKey: ['quiz', quizId, 'choices'],
queryFn: () => getChoicesOfQuiz(quizId),
}),
queryClient.fetchQuery(quizOptions.detail(quizId)),
queryClient.fetchQuery(quizOptions.choices(quizId)),
]);

return (
Expand Down
2 changes: 1 addition & 1 deletion app/quizzes/[id]/quiz.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import Button from '@/components/common/buttons/button';
import { useGetQuiz } from '@/hooks/quiz';
import { useGetQuiz } from '@/services/quiz/hooks';

export function Quiz({ id }: { id: number }) {
const { data: quiz } = useGetQuiz(id);
Expand Down
64 changes: 0 additions & 64 deletions hooks/quiz.ts

This file was deleted.

50 changes: 50 additions & 0 deletions services/quiz/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { createClient } from '@/utils/supabase/client';
import { SupabaseClient } from '@supabase/supabase-js';

const quizAPI = {
getQuiz: async (id: number) => {
const supabase: SupabaseClient<Database> = createClient();

const { data } = await supabase
.from('quizzes')
.select(`*, users!quizzes_user_id_fkey(id, name)`)
.eq('id', id)
.limit(1)
.single();

return data;
},

getChoicesOfQuiz: async (quizId: number) => {
const supabase: SupabaseClient<Database> = createClient();

const { data } = await supabase
.from('choices')
.select(`id, quiz_id, description`)
.eq('quiz_id', quizId);

return data;
},

postQuizSubmission: async (params: { quizId: number; choiceId: number }) => {
const { quizId, choiceId } = params;

const res = await fetch('/api/quiz-submission', {
method: 'POST',
body: JSON.stringify({
quizId,
choiceId,
}),
});

const json = await res.json();

if (!res.ok) {
throw new Error(json.error);
}

return json;
},
};

export default quizAPI;
17 changes: 17 additions & 0 deletions services/quiz/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useSuspenseQuery, useMutation } from '@tanstack/react-query';
import quizOptions from './options';
import quizAPI from './api';

export function useGetQuiz(quizId: number) {
return useSuspenseQuery(quizOptions.detail(quizId));
}

export function useGetChoicesOfQuiz(quizId: number) {
return useSuspenseQuery(quizOptions.choices(quizId));
}

export function useSubmitQuiz() {
return useMutation({
mutationFn: quizAPI.postQuizSubmission,
});
}
20 changes: 20 additions & 0 deletions services/quiz/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { queryOptions } from '@tanstack/react-query';
import quizAPI from './api';

const quizOptions = {
all: ['quizzes'] as const,

detail: (quizId: number) =>
queryOptions({
queryKey: [...quizOptions.all, 'detail', quizId],
queryFn: () => quizAPI.getQuiz(quizId),
}),

choices: (quizId: number) =>
queryOptions({
queryKey: [...quizOptions.detail(quizId).queryKey, 'choices'],
queryFn: () => quizAPI.getChoicesOfQuiz(quizId),
}),
};

export default quizOptions;

0 comments on commit 5fa7d31

Please sign in to comment.