From e0621b7e41e7e624f6111b15630665f212c59feb Mon Sep 17 00:00:00 2001 From: Aarni Halinen Date: Sun, 28 Mar 2021 18:03:36 +0300 Subject: [PATCH 1/2] update some admin pages to use swr hooks --- src/pages/admin/events/index.tsx | 14 +++-------- src/pages/admin/feed/index.tsx | 14 +++-------- src/pages/admin/jobads/index.tsx | 14 +++-------- src/pages/admin/signups/[id]/email.tsx | 14 +++++++---- src/pages/signup/edit/[id]/[uuid].tsx | 35 ++++++++++++++------------ 5 files changed, 40 insertions(+), 51 deletions(-) diff --git a/src/pages/admin/events/index.tsx b/src/pages/admin/events/index.tsx index a6830d6..f97becb 100644 --- a/src/pages/admin/events/index.tsx +++ b/src/pages/admin/events/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { NextPage } from "next"; import Head from "next/head"; import { formatRelative } from "date-fns"; @@ -6,7 +6,7 @@ import AdminListCommon from "@views/admin/AdminListCommon"; import { Link } from "@components/index"; import AddLink from "@components/AddLink"; import Event from "@models/Event"; -import EventApi from "@api/eventApi"; +import useFetchEvents from "@hooks/useFetchEvents"; const URL = "/admin/events"; @@ -37,13 +37,7 @@ const renderData = (events: Event[]) => { ); }; const AdminEventPage: NextPage = () => { - const [events, setEvents] = useState(null); - - useEffect(() => { - EventApi.getEvents({ auth: true }) - .then((res) => setEvents(res)); - }, []); - + const { data } = useFetchEvents({ options: { auth: true } }); return ( <> @@ -52,7 +46,7 @@ const AdminEventPage: NextPage = () => {

Events

- {renderData(events)} + {renderData(data)}
); diff --git a/src/pages/admin/feed/index.tsx b/src/pages/admin/feed/index.tsx index df4d964..514fee3 100644 --- a/src/pages/admin/feed/index.tsx +++ b/src/pages/admin/feed/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { NextPage } from "next"; import Head from "next/head"; import { formatRelative } from "date-fns"; @@ -6,7 +6,7 @@ import AdminListCommon from "@views/admin/AdminListCommon"; import { Link } from "@components/index"; import AddLink from "@components/AddLink"; import Post from "@models/Feed"; -import FeedApi from "@api/feedApi"; +import useFetchFeed from "@hooks/useFetchFeed"; const URL = "/admin/feed"; @@ -38,13 +38,7 @@ const renderData = (feed: Post[]) => { }; const AdminFeedPage: NextPage = () => { - const [forms, setForms] = useState(null); - - useEffect(() => { - FeedApi.getFeed({ auth: true }) - .then((res) => setForms(res)); - }, []); - + const { data } = useFetchFeed({ options: { auth: true } }); return ( <> @@ -53,7 +47,7 @@ const AdminFeedPage: NextPage = () => {

Feed

- {renderData(forms)} + {renderData(data)}
); diff --git a/src/pages/admin/jobads/index.tsx b/src/pages/admin/jobads/index.tsx index a781aa2..29c2c59 100644 --- a/src/pages/admin/jobads/index.tsx +++ b/src/pages/admin/jobads/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React from "react"; import { NextPage } from "next"; import Head from "next/head"; import { formatRelative } from "date-fns"; @@ -6,7 +6,7 @@ import AdminListCommon from "@views/admin/AdminListCommon"; import { Link } from "@components/index"; import AddLink from "@components/AddLink"; import JobAd from "@models/JobAd"; -import JobAdApi from "@api/jobAdApi"; +import useFetchJobAds from "@hooks/useFetchJobAds"; const URL = "/admin/jobads"; @@ -42,13 +42,7 @@ const renderData = (jobAds: JobAd[]) => { }; const AdminJobAdPage: NextPage = () => { - const [jobAds, setAds] = useState(null); - - useEffect(() => { - JobAdApi.getJobAds({ auth: true }) - .then((res) => setAds(res)); - }, []); - + const { data } = useFetchJobAds({ options: { auth: true } }); return ( <> @@ -57,7 +51,7 @@ const AdminJobAdPage: NextPage = () => {

Job advertisements

- {renderData(jobAds)} + {renderData(data)}
); diff --git a/src/pages/admin/signups/[id]/email.tsx b/src/pages/admin/signups/[id]/email.tsx index e578b86..13e56d5 100644 --- a/src/pages/admin/signups/[id]/email.tsx +++ b/src/pages/admin/signups/[id]/email.tsx @@ -44,12 +44,8 @@ const buildUISchema = () => ({ }, }); -const SignupEmailPage: NextPage = () => { +const useInitializeData = (id: string) => { const [signupForm, setSignupForm] = useState(null); - - const router = useRouter(); - const { id } = router.query; - useEffect(() => { const formId = Number(id); if (formId !== undefined) { @@ -58,6 +54,14 @@ const SignupEmailPage: NextPage = () => { } }, [id]); + return signupForm; +}; + +const SignupEmailPage: NextPage = () => { + const router = useRouter(); + const { id } = router.query; + const signupForm = useInitializeData(id as string); + const [error, setError] = useState(null); const [statusMessage, setStatusMessage] = useState(null); diff --git a/src/pages/signup/edit/[id]/[uuid].tsx b/src/pages/signup/edit/[id]/[uuid].tsx index 1cf2c1a..6413d78 100644 --- a/src/pages/signup/edit/[id]/[uuid].tsx +++ b/src/pages/signup/edit/[id]/[uuid].tsx @@ -14,33 +14,27 @@ interface QueryParams { const parseQueryParams = (query: QueryParams) => { const { id, uuid } = query; - return { signupId: id ? Number(id) : undefined, uuid, }; }; -const EditSignUpPage: NextPage = () => { - const router = useRouter(); - const { signupId, uuid } = parseQueryParams(router.query); - const [signUpForm, setForm] = useState(null); +const useFetchSignup = (signupId: number, uuid: string) => { + const [form, setForm] = useState(null); const [formData, setFormData] = useState({}); - const [statusMessage, setStatus] = useState(null); useEffect(() => { const fetchSignUpForm = async (id: number): Promise => { - const formPromise = SignupApi.getForm(id); - formPromise.then((form) => { - setForm(form); - }); - return formPromise; + const signupForm = await SignupApi.getForm(id); + setForm(signupForm); + return signupForm; }; const fetchSignUp = async (id: number, uniqueId: string): Promise => { - const signupPromise = SignupApi.getSignupUUID(id, uniqueId); - signupPromise.then((signup) => setFormData(signup.answer)); - return signupPromise; + const signup = await SignupApi.getSignupUUID(id, uniqueId); + setFormData(signup.answer); + return signup; }; if (signupId) { @@ -48,10 +42,19 @@ const EditSignUpPage: NextPage = () => { } }, [signupId, uuid]); + return [form, formData]; +}; + +const EditSignUpPage: NextPage = () => { + const router = useRouter(); + const { signupId, uuid } = parseQueryParams(router.query); + const [statusMessage, setStatus] = useState(null); + const [form, formData] = useFetchSignup(signupId, uuid); + const onSubmit = async (data) => { const payload: Signup = { id: Number(signupId), - signupForm_id: signUpForm.id, + signupForm_id: form.id, answer: data.formData, }; @@ -68,7 +71,7 @@ const EditSignUpPage: NextPage = () => { return ( Date: Sun, 28 Mar 2021 20:32:07 +0300 Subject: [PATCH 2/2] fix infinite fetching loop --- package-lock.json | 1 + package.json | 1 + src/hooks/useFetchEvents.ts | 11 ++++++++++- src/hooks/useFetchFeed.ts | 11 ++++++++++- src/hooks/useFetchJobAds.ts | 13 +++++++++++-- 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8990988..283f7ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@next/bundle-analyzer": "10.0.7", "axios": "0.21.1", "date-fns": "2.18.0", + "fast-deep-equal": "^3.1.3", "js-cookie": "2.2.1", "lodash": "4.17.21", "next": "10.0.8", diff --git a/package.json b/package.json index a895588..6f6fdb9 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "@next/bundle-analyzer": "10.0.7", "axios": "0.21.1", "date-fns": "2.18.0", + "fast-deep-equal": "^3.1.3", "js-cookie": "2.2.1", "lodash": "4.17.21", "next": "10.0.8", diff --git a/src/hooks/useFetchEvents.ts b/src/hooks/useFetchEvents.ts index 627140e..5c32d00 100644 --- a/src/hooks/useFetchEvents.ts +++ b/src/hooks/useFetchEvents.ts @@ -1,4 +1,6 @@ +import { useRef } from "react"; import useSWR from "swr"; +import isDeepEqual from "fast-deep-equal/react"; import axios, { AxiosRequestConfig } from "axios"; import Event from "@models/Event"; import { getAuthHeader } from "@utils/auth"; @@ -34,7 +36,14 @@ const useFetchEvents = ({ options = {}, }: FetchArguments) => { const { url, config } = generateFetchParams(id, options); - const { data, error } = useSWR([url, config], fetcher, { initialData }); + + // Use ref, since config dependency is non-primitive => without this we have infinite fetch loop + const configRef = useRef(config); + if (!isDeepEqual(configRef.current, config)) { + configRef.current = config; + } + + const { data, error } = useSWR([url, configRef.current], fetcher, { initialData }); return { data: data?.results || data, error, diff --git a/src/hooks/useFetchFeed.ts b/src/hooks/useFetchFeed.ts index ce8fa57..1124a46 100644 --- a/src/hooks/useFetchFeed.ts +++ b/src/hooks/useFetchFeed.ts @@ -1,4 +1,6 @@ +import { useRef } from "react"; import useSWR from "swr"; +import isDeepEqual from "fast-deep-equal/react"; import axios, { AxiosRequestConfig } from "axios"; import Post from "@models/Feed"; import { getAuthHeader } from "@utils/auth"; @@ -30,7 +32,14 @@ const useFetchFeed = ({ options = {}, }: FetchArguments) => { const { url, config } = generateFetchParams(id, options); - const { data, error } = useSWR([url, config], feedFetcher, { initialData }); + + // Use ref, since config dependency is non-primitive => without this we have infinite fetch loop + const configRef = useRef(config); + if (!isDeepEqual(configRef.current, config)) { + configRef.current = config; + } + + const { data, error } = useSWR([url, configRef.current], feedFetcher, { initialData }); return { data: data?.results || data, error, diff --git a/src/hooks/useFetchJobAds.ts b/src/hooks/useFetchJobAds.ts index 96a7404..100363a 100644 --- a/src/hooks/useFetchJobAds.ts +++ b/src/hooks/useFetchJobAds.ts @@ -1,5 +1,7 @@ -import axios, { AxiosRequestConfig } from "axios"; +import { useRef } from "react"; import useSWR from "swr"; +import isDeepEqual from "fast-deep-equal/react"; +import axios, { AxiosRequestConfig } from "axios"; import JobAd from "@models/JobAd"; import { getAuthHeader } from "@utils/auth"; import { URL, Options } from "@api/jobAdApi"; @@ -30,7 +32,14 @@ const useFetchJobAds = ({ options = {}, }: FetchArguments) => { const { url, config } = generateFetchParams(id, options); - const { data, error } = useSWR([url, config], jobAdFetcher, { initialData }); + + // Use ref, since config dependency is non-primitive => without this we have infinite fetch loop + const configRef = useRef(config); + if (!isDeepEqual(configRef.current, config)) { + configRef.current = config; + } + + const { data, error } = useSWR([url, configRef.current], jobAdFetcher, { initialData }); return { data: data?.results || data, error,