Search code examples
reactjstypescriptmodal-dialogchakra-ui

How can I make my modal dialog's content only render once when opened?


I've used material UI before and didn't seem to run into this problem. The problem is this: when I use the chakra ui modal dialog, when it opens, all the components inside it are rendered twice. I tried to disable animation motionPreset={'none'} but it didn't help.

Here is my code:

import "./styles.css";
import { useEffect } from "react";
import {
  Box,
  Button,
  Modal,
  ModalBody,
  ModalCloseButton,
  ModalContent,
  ModalFooter,
  ModalHeader,
  ModalOverlay,
  useDisclosure
} from "@chakra-ui/react";

const TestAppear = () => {
  useEffect(() => {
    console.log("TestAppear mounted");
  }, []);
  return <Box>Appear</Box>;
};

export default function App() {
  const { isOpen, onOpen, onClose } = useDisclosure();

  return (
    <div className="App">
      <Button onClick={onOpen}>Open</Button>
      <Modal isOpen={isOpen} onClose={onClose} motionPreset={"none"}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <TestAppear />
          </ModalBody>

          <ModalFooter>
            <Button colorScheme="blue" mr={3} onClick={onClose}>
              Close
            </Button>
            <Button variant="ghost">Secondary Action</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </div>
  );
}

The TestAppear component clearly shows the problem. If you look at the console, you will see that when the dialog is opened, it is rendered twice.

Code example at codesandbox

I am doing a project for parking lots. When you click on a parking space, a dialog pops up showing the booked time slots. The list of intervals is a separate component, within which they are loaded from the server. Therefore, if this component with a list of intervals is mounted twice, then the request will be sent to the server twice, which is very bad.


Solution

  • As Jacob has said, this behavior is due to strict mode of react. If you go to index.js file where the root is rendered, you can see that the element is wrapped inside the <React.StrictMode>. This wrapper is the cause that everything you do inside the useEffect with empty dependency array is called twice. You can simply remove this strict mode wrapper and see the difference.