0

I'm using create-react-app and a couple external modules, like react-markdown. How do I 'require' a dynamically generated filename? I have thousands of markdown files and I need to load them on the fly. My component works fine if I supply a static path, but doesn't work when I try to concatenate the path. I can't import statically because there are too many files.

Example:

import React, { useState, useEffect } from "react";
import ReactMarkdown from "react-markdown/with-html"

const MarkdownServer = ({myMarkdownFilename}) => {  
  const [markdown, setValue] = useState([]);

  useEffect(() => {
    async function fetchData() {
      // this works...
      // const p = require("../../assets/markdown/mymarkdown.md") 

      // this does not.
      const p = require("../../assets/markdown/" + myMarkdownFilename);
      const markdown = await fetch(p).then(res => res.text());
      setValue(markdown);
    }
    fetchData();
  }, []);

  return (
    <p>
      <ReactMarkdown source={ markdown } escapeHtml={false} />
    </p>
  );
};

export default MarkdownServer;

Error:

Unhandled Rejection (Error): Cannot find module '../../assets/markdown/mymarkdown.md'

2 Answers 2

1

Have you tried using React's lazy and suspense API's for dynamic importing?

I haven't gotten to use it myself yet but it seems like you could replace the require() with something like const p = React.lazy(() => import("../../assets/markdown/" + myMarkdownFilename)); and then have the return statement render the ReactMarkdown component inside of a Suspense component.

If you're using webpack you might also want to look here:

Let me know how it goes!

Sign up to request clarification or add additional context in comments.

2 Comments

Lazy import should do job but you don't need Suspense for it.
Just tried this, along with the "loadable" module, and neither work.
1

Solution is to create a context module with the Webpack context module API. With a bit of regex, I was able to require the markdown docs.

After my standard imports:

const cache = {};

function importAll (r) {
  r.keys().forEach(key => cache[key] = r(key));
}

importAll(require.context("../../assets/markdown/", true, /\.md$/)); 

The cache object now contains the required files and their hashed names as name:value pairs. In my code I simply build the filename and use it to access the property:

 useEffect(() => {
    async function fetchData() {
      filepath = "../../assets/markdown/" + myMarkdownFilename
      const p = cache[filepath]
      const markdown = await fetch(p).then(res => res.text());
      setValue(markdown);
    }
    fetchData();
  }, []);

This being said, there really ought to be a saner way to import static assets.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.