1

I am trying an experiment to generate a random component. I have a sandbox with a basic example of this idea.

https://codesandbox.io/s/practical-kalam-mz9sdy?file=/src/App.js

For example I have components named like :

const Test0 = () => {
    return (
      <>
        <p> Hello, I am default text</p>
      </>
    );
  };

  const Test1 = () => {
    return (
      <>
        <p>Text String #1</p>
      </>
    );
  };
  const Test2 = () => {
    return (
      <>
        <p>Text String #2</p>
      </>
    );
  };

And a button that will randomly generate which of those components to use by its function name.

const handleClick = (e) => {
    let num = getRandomNumber(1, 5);
    let newString = "Test" + num;

    // without eval it just prints the string, eg: Test1
    // setOutput(newString);
    setOutput(eval(newString));
  };

Using eval works for this but I am sure there is a better way to activate the component. Just passing the function name for the initial state works fine :

  const [output, setOutput] = useState(Test0);

The second test I trying to run the typing function with the dynamic component. You can see it the example it runs fine with the initial value of useState but then when clicking the test I see no errors and I see expected console log but the typing output is not activated in the same way the test components are swapped out.

// The typing component will not work with this method

 const Direction4 = () => {
    return (
      <Typist typingDelay={50} cursor={<span className="cursor">|</span>}>
        This is another amazing dirction description.
      </Typist>
    );
  };

Thanks for any suggestions on how I could render components dynamically this way.

Zac
  • 12,637
  • 21
  • 74
  • 122

1 Answers1

1

No need for strings and eval and all of that, you can put your components in an array and pick one right from there:

const Test1 = () => <p>Text String #1</p>
const Test2 = () => <p>Text String #2</p>
const testComponents = [Test1, Test2]
const handleClick = () => {
  const randomElement = testComponents[
    Math.floor(Math.random() * testComponents.length)
  ]
  setOutput(randomElement)
}

The same logic can be used for the second section:

const directions = [DefaultType, Direction1]
const handleClickTwo = () => {
  const randomElement = directions[
    Math.floor(Math.random() * directions.length)
  ]
  setOutputTwo(randomElement);
};
Zac Anger
  • 6,983
  • 2
  • 15
  • 42
  • Thank you! This works great for the text components and much better than using `eval` but still same behavior for the Typing components. I updated the sandbox so you can see what I mean. – Zac Dec 19 '22 at 00:35
  • Ah, that seems to be an issue with that Typing component. I was able to get it to work by throwing in a little `sleep` (there's a good version of that [here](https://stackoverflow.com/a/951057/5774952)) and calling `setOutputTwo` in the `.then`. I don't know anything about that library but I'm guessing there's something in their timing code that's causing that problem. – Zac Anger Dec 19 '22 at 00:42
  • Interesting — that solution also is inconsistent. It might make more sense to swap out the innertext rather than the entire element there, there seems to be an issue with either React not realizing the node changed, or the Typing component itself. – Zac Anger Dec 19 '22 at 00:50
  • Interesting I will play around with the timing to see if I have any positive results. I was thinking of going down the useEffect route to see if maybe when I changed that state value I could cause the new typing component to render that way. – Zac Dec 19 '22 at 00:56