Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`forwardRef` error when dynamically importing a module in Next.js

I'm trying to use forwardRef on an external component but ref.current is not the actual ref. Not sure if I'm missing something.

I'm doing:

const Editor = dynamic(() => import("react-markdown-editor-lite"), {
  ssr: false
});

const ForwardedRefEditor = forwardRef((props, ref) => (
  <Editor {...props} ref={ref} />
));

-----

const editorRef = useRef(null);
<ForwardedRefEditor
  ref={editorRef}
  value={content}
  onChange={({ text }) => setContent(text)}
/>

Full code: https://codesandbox.io/s/objective-benz-qh4ec?file=/pages/index.js:63-73

More info: https://github.com/vercel/next.js/issues/4957

like image 837
Mati Tucci Avatar asked Aug 18 '20 13:08

Mati Tucci


People also ask

What is next dynamic import?

Examples. Next. js supports lazy loading external libraries with import() and React components with next/dynamic . Deferred loading helps improve the initial loading performance by decreasing the amount of JavaScript necessary to render the page.

How do I import component dynamically into React?

In React, dynamically importing a component is easy—you invoke React. lazy with the standard dynamic import syntax and specify a fallback UI. When the component renders for the first time, React will load that module and swap it in.

What is dynamic import in JavaScript?

Dynamic imports or Code Splitting is the practice of breaking up your JavaScript modules into smaller bundles and loading them dynamically at runtime to improve and increase the performance of your website dramatically.


1 Answers

You need to wrap your component in a custom component.

Using forwardRef:

Wrap your Editor component:

import React from "react";
import Editor from "react-markdown-editor-lite";

export default function WrappedEditor({ editorRef, ...props }) {
  return <Editor {...props} ref={editorRef} />;
}

And then import it dynamically:

import dynamic from "next/dynamic";
import { useRef, useState, forwardRef } from "react";

const Editor = dynamic(() => import("../WrappedEditor"), {
  ssr: false
});

const ForwardRefEditor = forwardRef((props, ref) => 
  <Editor {...props} editorRef={ref}/>
)

export default function IndexPage() {
  const editorRef = useRef(null);
  const [content, setContent] = useState("");

  console.log("editorRef", editorRef.current);

  return (
    <ForwardRefEditor
      ref={editorRef}
      value={content}
      onChange={({ text }) => setContent(text)}
    />
  );
}

CodeSandbox

You can also use the custom prop approach without using forwardRef.

Custom prop

Wrap your Editor component exactly as in the previous example:

import React from "react";
import Editor from "react-markdown-editor-lite";

export default function WrappedEditor({ editorRef, ...props }) {
  return <Editor {...props} ref={editorRef} />;
}

Pass the custom ref prop to the Editor component:

import dynamic from "next/dynamic";
import { useRef, useState } from "react";

const Editor = dynamic(() => import("../WrappedEditor"), {
  ssr: false
});

export default function IndexPage() {
  const editorRef = useRef(null);
  const [content, setContent] = useState("");

  console.log("editorRef", editorRef.current);

  return (
    <Editor
      editorRef={editorRef}
      value={content}
      onChange={({ text }) => setContent(text)}
    />
  );
}

CodeSandbox

like image 98
fcFn Avatar answered Oct 23 '22 05:10

fcFn