mshumayl / ai-daleel

AI Daleel source code.
https://ai-daleel.vercel.app
1 stars 0 forks source link

Fix issue with localStorage #7

Closed mshumayl closed 1 year ago

mshumayl commented 1 year ago

Commit f18609c introduced an error that brings the whole site down.

Error: SyntaxError: Unexpected end of JSON input

Culprit:

const [aiResponse, setAiResponse] = useState<responseType>(() => {
        const cachedResponse = (typeof window !== "undefined") ? localStorage.getItem("cached_response") : ""; //Because of SSR, need to check if code is running client-side
    const parsedCachedResponse = JSON.parse(cachedResponse || "");
    // eslint-disable-next-line @typescript-eslint/no-unsafe-return
    return parsedCachedResponse || defaultResponse;
  });

If JSON.parse(cachedResponse || "{}"), map error occurs:

TypeError: aiResponse.map is not a function

This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source
src\components\PromptInput.tsx (88:23) @ map

  86 | </form>
  87 | <ul className="flex flex-col items-center w-full">
> 88 |    {aiResponse.map(({ surah, verse }) => {
     |               ^
  89 |       return ((displayLoader) ? 
  90 |         (<div key={`${surah}_${verse}`} className="animate-ping font-zilla-slab-italic text-xs h-max w-max text-slate-500 my-4 rounded-lg bg-slate-200 py-1 px-2">Thinking...</div>) : (surah !== 0 ) ?
  91 |         (<VerseCard surah={surah} verse={verse}/

If JSON.parse(cachedResponse || "[{"surah": 0, "verse": 0}, {"surah": 0, "verse": 0}, {"surah": 0, "verse": 0}]"), hydration error occurs:

Unhandled Runtime Error
Error: Hydration failed because the initial UI does not match what was rendered on the server.

See more info here: https://nextjs.org/docs/messages/react-hydration-error
mshumayl commented 1 year ago

https://stackoverflow.com/questions/73944543/react-custom-localstorage-hook-hydration-error-in-nextjs https://github.com/pmndrs/zustand/issues/938#issuecomment-1141402342

mshumayl commented 1 year ago

Can consider JSON.parse(cachedResponse || "{}"), then check if aiResponse contains any value before rendering. Hopefully this won't cause any hydration issues too.

mshumayl commented 1 year ago

Doing the above with the following code:

{aiResponse && aiResponse.map && aiResponse.map(({ surah, verse }) => {
     return ((displayLoader) ? 
          (<div key={`${surah}_${verse}`} className="animate-ping font-zilla-slab-italic text-xs h-max w-max text-slate-500 my-4 rounded-lg bg-slate-200 py-1 px-2">Thinking...</div>) : (surah !== 0 ) ?
          (<VerseCard surah={surah} verse={verse}/>) 
          : (<></>)
      );}
 )}

Yields hydration issues.

mshumayl commented 1 year ago

Seems to be fixed by dynamically importing VerseCard with SSR set to false.

import dynamic from 'next/dynamic';
const VerseCard = dynamic(() => import("./VerseCard"), {ssr: false})

This makes perfect sense as VerseCard will never need to be rendered on server-side.

To recap, the hydration issue comes from the fact that the number of VerseCards rendered on client-side is always more than the number of VerseCards rendered on server-side.