To findout CSR, SSR in next.js

338 Views Asked by At

I'm using package.json

    "next": "13.0.6",
    "react": "18.2.0",
    "react-dom": "18.2.0",

Case 1

const [domLoaded, setDomLoaded] = useState(false);

  useEffect(() => {
    setDomLoaded(true);
  }, []);

...

{domLoaded && (
        <>
          // ...something need to be rendered after DOM loaded like below
          <Toolbar />
          <ReactQuill
            ref={quillRef}
            value={value}
            modules={modules}
            onChange={setValue}
            theme="snow"
          />
        </>
      )}

Case2

{typeof window !== "undefined" && (
        <>
          <Toolbar />
          <ReactQuill
            ref={quillRef}
            style={{ height: "60vh" }}
            value={value}
            modules={modules}
            onChange={setValue}
            theme="snow"
          />
        </>
      )}

I think Case 1 is same as Case 2 but, Case1 is ok, but Case2 throw an Error says that "Error: Hydration failed because the initial UI does not match what was rendered on the server."

What is difference between two cases ? Thanks.

Entire code

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import "react-quill/dist/quill.snow.css";

const ReactQuill = typeof window === "object" ? require("react-quill") : () => false;
const Quill = typeof window === "object" ? require("react-quill").Quill : () => false;

import Toolbar from "~/@components/molecule/Editor/Toolbar";
import useInput from "~/@hooks/useInput";
import { ContentService } from "~/@services/content";

export default function QuillTest() {
  const quillRef = useRef();
  const [value, setValue] = useState();
  const [domLoaded, setDomLoaded] = useState(false);

  useEffect(() => {
    setDomLoaded(true);
  }, []);

  const imageHandler = useCallback(() => {
    const formData = new FormData();

    const input = document.createElement("input");
    input.setAttribute("type", "file");
    input.setAttribute("accept", "image/*");
    input.setAttribute("name", "image");
    input.click();

    input.onchange = async () => {
      if (input.files && input.files[0]) {
        console.log("input.files[0]", input.files[0]);
        formData.append("image", input.files[0]);
      }

      const url = await ContentService.imageUpload(formData);
      //@ts-ignore
      const quill = quillRef.current.getEditor();
      const range = quill.getSelection()?.index;

      if (typeof range !== "number") return;
      quill.setSelection(range, 1);
      quill.clipboard.dangerouslyPasteHTML(range, `<img src=${url} alt="image" />`);
    };
  }, [quillRef]);

  const devideHandler = useCallback(() => {
    let BlockEmbed = Quill.import("blots/block/embed");
    class DividerBlot extends BlockEmbed {}

    DividerBlot.blotName = "divider2";
    DividerBlot.tagName = "hr";

    Quill.register("formats/divider", DividerBlot);

    //@ts-ignore
    const quill = quillRef.current?.getEditor();
    let range = quill.getSelection(true);
    quill.insertText(range.index, "\n", Quill.sources.USER);
    quill.insertEmbed(range.index + 1, "divider2", true, Quill.sources.USER);
    quill.setSelection(range.index + 2, Quill.sources.SILENT);
  }, [quillRef]);

  const modules = useMemo(() => {
    return {
      toolbar: {
        container: "#toolbar",
        handlers: {
          image: imageHandler,
          divider2: devideHandler,
        },
      },
    };
  }, [imageHandler]);

  return (
    <div>
      {typeof window !== "undefined" && (
        <>
          <Toolbar />
          <ReactQuill
            ref={quillRef}
            style={{ height: "60vh" }}
            value={value}
            modules={modules}
            onChange={setValue}
            theme="snow"
          />
        </>
      )}
    </div>
  );
}
0

There are 0 best solutions below