0

I'm trying to create multiple React components dynamically from a string entered by the user. Each letter is supposed to be rendered as a separate component.

My plan was to create an array from the string and map over it returning each component in a new array.

My main issue is to convert the letter (string) into the component's name.

import React from 'react'
import './App.css'

import A from './ComponentA'
import B from './ComponentB'
import C from './ComponentC'

const userInput = "ABC"
const userInputArray = userInput.split("")

const components = userInputArray.map((comp, index) => {
  return (
    React.createElement(eval(comp), { key: [index]})
  )
})

function App() {

  return (
    <>
    { components }
    </>
  )
}

export default App

I thought I found a solution using 'eval' but it fails when minified in production.

(Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: number.)

2
  • 2
    You have a string and want an associated value - why not use an object? Commented Feb 9, 2023 at 21:29
  • 1
    I think you should create a function with switch statement that will return the needed component for each letter. Commented Feb 9, 2023 at 21:35

1 Answer 1

2

There is really no reason to use eval() at all: you can simply use a dictionary to store your component references, and then render it accordingly.

Note that I have used i as a key, but you really should not use it but instead rely on a stable, unique identifier.

import A from "./ComponentA";
import B from "./ComponentB";
import C from "./ComponentC";

// NOTE: Added 'D' to test for component that doesn't exist
const userInput = "ABCD";
const userInputArray = userInput.split("");

const components = { A, B, C };

export default function App() {
  return userInputArray.map((component, i) => {
    const Component = components[component];
    if (!Component) return <></>;

    return <Component key={i} />;
  });
}

Edit Dynamic components

However, what I would recommend is that you take advantage of React lazy loading and suspense feature, so that you don't have to load all components at once if the user input array does not need all of them. It is little more advanced, but here is another proof-of-concept example:

  • It allows you to input dynamic string that is parsed to load in the necessary components
  • It uses React.Suspense + React.lazy to perform dynamic loading
import { lazy, Suspense, useState } from "react";

const components = {
  A: lazy(() => import("./ComponentA")),
  B: lazy(() => import("./ComponentB")),
  C: lazy(() => import("./ComponentC"))
};

export default function App() {
  const [userInput, setUserInput] = useState("ABCD");
  return (
    <>
      <input
        type="text"
        value={userInput}
        onChange={(e) => setUserInput(e.currentTarget.value)}
      />
      <Suspense fallback="Loading...">
        {userInput.split("").map((component, i) => {
          const Component = components[component.toUpperCase()];
          if (!Component) return <></>;

          return <Component key={i} />;
        })}
      </Suspense>
    </>
  );
}

Edit Dynamic components with lazy loading

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

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.