0

here is the code:

import { type AppType } from 'next/app'
import { api } from '~/utils/api'
import '~/styles/globals.css'
import Nav from '~/components/Nav'
import { useEffect, useState } from 'react'

const MyApp: AppType = ({ Component, pageProps }) => {
  const [showCart, setShowCart] = useState(false)
  const [loading, setLoading] = useState(false)

  const createSession = api.user.createSession.useMutation()

  const generateId = async (): Promise<string | undefined> => {
    const res = await createSession.mutateAsync()
    if (res.response) {
      return res.response.id
    } else if (res.error) {
      return res.error
    }
  }

  const setSessionId = async () => {
    const tmp: string | undefined = await generateId()
    if (tmp) document.cookie = `sessionId=${tmp}`
    setLoading(false)
  }

  useEffect(() => {
    if (!loading) {
      setLoading(true)
      const cookieString: string = document.cookie

      const cookies: string[] = cookieString.split(';') || []

      let sessionId: string | null = null

      for (let i = 0; i < cookies.length; i++) {
        const cookie: string | undefined = cookies[i]

        if (!cookie || cookie.trim() === '') {
          continue
        }

        if (cookie.trim().startsWith('sessionId=')) {
          sessionId = cookie.trim().substring('sessionId='.length)
          break
        }
      }
      if (!sessionId) {
        void setSessionId()
      }
    }
  }, [])

  return (
    <>
      <Nav showCart={showCart} setShowCart={setShowCart} />
      <h1>{loading && 'LOADING'}</h1>
      <Component {...pageProps} />
    </>
  )
}

export default api.withTRPC(MyApp)

I am using trpc and prisma. for some reason the useEffect is running twice and it is posting to the db twice. any idea what's causing the re-render? the loading is always false for some reason even after I set it to true. is it because I am setting it wrong? I initially thought that maybe it was rendering twice, once in the server and once in the client. but I logged the window object and they were both defined.

any idea what I am doing wrong?

  • 2
    Is it running twice when deployed? Or only when running locally (or in tests)? If locally, are you using React's `StrictMode` for rendering? Strict mode will render the component twice locally, by design. See https://react.dev/reference/react/StrictMode – aarowman May 31 '23 at 20:32

2 Answers2

0

I think this wokr once setLoadin to true , and update useEffect

import { type AppType } from 'next/app';
import { api } from '~/utils/api';
import '~/styles/globals.css';
import Nav from '~/components/Nav';
import { useEffect, useState } from 'react';

const MyApp: AppType = ({ Component, pageProps }) => {
  const [showCart, setShowCart] = useState(false);
  const [loading, setLoading] = useState(true); // Set initial loading state to true

  const createSession = api.user.createSession.useMutation();

  const generateId = async (): Promise<string | undefined> => {
    const res = await createSession.mutateAsync();
    if (res.response) {
      return res.response.id;
    } else if (res.error) {
      return res.error;
    }
  };

  const setSessionId = async () => {
    const tmp: string | undefined = await generateId();
    if (tmp) document.cookie = `sessionId=${tmp}`;
    setLoading(false);
  };

  useEffect(() => {
    const getSessionId = () => {
      const cookieString: string = document.cookie;
      const cookies: string[] = cookieString.split(';') || [];

      let sessionId: string | null = null;

      for (let i = 0; i < cookies.length; i++) {
        const cookie: string | undefined = cookies[i];

        if (!cookie || cookie.trim() === '') {
          continue;
        }

        if (cookie.trim().startsWith('sessionId=')) {
          sessionId = cookie.trim().substring('sessionId='.length);
          break;
        }
      }

      return sessionId;
    };

    if (!getSessionId()) {
      setSessionId();
    } else {
      setLoading(false); // Set loading to false  present
    }
  }, []);

  return (
    <>
      <Nav showCart={showCart} setShowCart={setShowCart} />
      {loading && <h1>LOADING</h1>} {/* Show LOADING only if loading is true */}
      <Component {...pageProps} />
    </>
  );
};

export default api.withTRPC(MyApp);
Zakaria Zhlat
  • 320
  • 1
  • 6
  • 21
0

The issue could be related to the Next.js server-side rendering (SSR) behavior. When using Next.js, the initial rendering occurs on the server and then the component rehydrates on the client side.

To address this, you can add a condition in your useEffect to only execute the code on the client side. You can do this by checking if typeof window is defined, as the window object is only available in the browser environment.

Here's an updated version of your code with the modification:

// ...

useEffect(() => {
  if (typeof window !== 'undefined' && !loading) {
    setLoading(true);
    const cookieString = document.cookie;
    // Rest of your code...
  }
}, []);

// ...

By adding typeof window !== 'undefined' as a condition, the code inside the useEffect will only run on the client side and prevent it from executing on the server side during the initial render. This should help eliminate the duplicated database posts.

Regarding the issue with loading always being false, it could be because setLoading(true) is asynchronous, and the subsequent line if (!loading) may not reflect the updated value immediately. If you need to check the value of loading, you can add a console.log statement after setLoading(true) to see if it changes accordingly.

Make sure to test your code with this modification and verify if the behavior is as expected.

Bandantonio
  • 816
  • 1
  • 7
  • 19
  • `useEffect` always runs on the client-side, never on the server-side. Inside a `useEffect` the `window` object will always be defined. – ivanatias Jun 01 '23 at 01:01