Next.js Discord

Discord Forum

Auto hide based on state dosen't work in Next, but in pure react app

Unanswered
American Crocodile posted this in #help-forum
Open in Discord
Avatar
American CrocodileOP
I'm trying to write a survey banner that should only be shown if a user has not answered. I first wrote it in a "pure" react app, after transferring it to NextJS the logic for hiding the survey no longer works.

When a user presses the "Yes" or "No" button the survey should immediatly be persistently hidden, but I have to press one of the buttons many times, then wait a few seconds before my pop up hides. Why is this? Is there something next specific I'm not getting?

import { useState, useEffect } from 'react';
import { Button } from '@ruter-ds-next/button';
import { useTranslation } from 'next-i18next';

const Survey = () => {
  const [showSurveyLink, setShowSurveyLink] = useState(true);
  const { t } = useTranslation(['common']);

  useEffect(() => {
    const response = localStorage.getItem('surveyResponse');
    if (response) {
      setShowSurveyLink(false);
    }

  }, []);

  const handleResponse = (response) => {
    localStorage.setItem('surveyResponse', response);
    setShowSurveyLink(false);
  };
  const SurveyBoxTemplate = () => (
    <div className="fixed flex flex-col md:bottom-2 md:right-2 bottom-0 right-0 w-full md:w-96 bg-light-fill-quinary-solid bg-fill-quinary-solid dark:bg-dark-fill-primary z-10 p-6 text-dark-background-primary dark:text-dark-ink-primary shadow-raised">
      <h2 className="text-body-m-reg pb-6">{t('survey')}</h2>
      <div className="flex space-x-4">
        <Button
          as="a"
          href="My amazing form"
          target="_blank"
          rel="noopener noreferrer"
          onClick={() => handleResponse('yes')}
          className="w-1/2 text-center"
        >
          {t('yes')}
        </Button>
        <Button
          skin="SECONDARY"
          onClick={() => handleResponse('no')}
          className="w-1/2"
        >
          {t('no')}
        </Button>
      </div>
    </div>
  );

  return <>{showSurveyLink && <SurveyBoxTemplate />}</>;
};

export default Survey;

13 Replies

Avatar
try adding a mounted state
import { useState, useEffect } from 'react';
import { Button } from '@ruter-ds-next/button';
import { useTranslation } from 'next-i18next';

const Survey = () => {
  const [showSurveyLink, setShowSurveyLink] = useState(true);
  const { t } = useTranslation(['common']);
  const [mounted, setMounted] = useState(false)

  useEffect(() => {
    setMounted(true)
    const response = localStorage.getItem('surveyResponse');
    if (response) {
      setShowSurveyLink(false);
    }

  }, []);

  const handleResponse = (response) => {
    localStorage.setItem('surveyResponse', response);
    setShowSurveyLink(false);
  };
  const SurveyBoxTemplate = () => (
    <div className="fixed flex flex-col md:bottom-2 md:right-2 bottom-0 right-0 w-full md:w-96 bg-light-fill-quinary-solid bg-fill-quinary-solid dark:bg-dark-fill-primary z-10 p-6 text-dark-background-primary dark:text-dark-ink-primary shadow-raised">
      <h2 className="text-body-m-reg pb-6">{t('survey')}</h2>
      <div className="flex space-x-4">
        <Button
          as="a"
          href="My amazing form"
          target="_blank"
          rel="noopener noreferrer"
          onClick={() => handleResponse('yes')}
          className="w-1/2 text-center"
        >
          {t('yes')}
        </Button>
        <Button
          skin="SECONDARY"
          onClick={() => handleResponse('no')}
          className="w-1/2"
        >
          {t('no')}
        </Button>
      </div>
    </div>
  );

  return <>{mounted && showSurveyLink && <SurveyBoxTemplate />}</>;
};

export default Survey;
Avatar
American CrocodileOP
Thanks for the suggestion @Ray ! I tried mounted state earlier, and updated the code to your suggestion now. but the same issue persist, I have to press the buttons multiple times and wait for anything to happen
Avatar
 <Button
          href="My amazing form"
          target="_blank"
          rel="noopener noreferrer"
          onClick={() => handleResponse('yes')}
          className="w-1/2 text-center"
        >
          {t('yes')}
        </Button>

try removing the as props
Avatar
American CrocodileOP
same issue happens with the "No" button that is not as='a'
Avatar
how about turning <Button /> to <button> ?
Avatar
American CrocodileOP
Tried it now, same issue persists. <Button> renders a pure <button> (unless it has as-prop) with some styling
Avatar
could you record a video?
Avatar
American CrocodileOP
sure, give me a few seconds to figure out how to record a video first 🙂
Avatar
cmd + shift + 5 on mac
win + alt + r on window
Avatar
American CrocodileOP
Image
Avatar
try this
import { flushSync } from "react-dom";

  const handleResponse = (response) => {
    localStorage.setItem("surveyResponse", response);
    flushSync(() => {
      setShowSurveyLink(false);
    });
  };
Avatar
American CrocodileOP
have some meetings now, thanks will try this as soon as I can 🙂
the top of the react docs looked a bit scary
"Using flushSync is uncommon and can hurt the performance of your app."
Avatar
I think its fine for this use case if it works