Home > Software design >  What is the proper way of rendering a component passed as a prop? And how to extend it?
What is the proper way of rendering a component passed as a prop? And how to extend it?

Time:05-30

Let's say we have a simple function component:

const Dummy = () => {
  return <div>Dummy</div>;
};

export default Dummy;

And let's say that we pass this component as a prop to another component:

<App
   componentA={<Dummy />}
   componentB={Dummy}
/>

And here's the App.js code:

import React from "react";

export default function App({ componentA, componentB }) {
  const ComponentA = componentA;
  const ClonedA = React.cloneElement(componentA, { anotherProp: "yes" });
  const CreatedA = React.createElement(componentA, { anotherProp: "yes" });
  const ComponentB = componentB;
  const ClonedB = React.cloneElement(componentB, { anotherProp: "yes" });
  const CreatedB = React.createElement(componentB, { anotherProp: "yes" });
  return (
    <div>
      {/* <ComponentA /> */}
      {/* <ClonedA /> */}
      {/* <CreatedA /> */}
      <ComponentB />
      {/* <ClonedB /> */}
      {/* <CreatedB /> */}
    </div>
  );
}

How can I extend componentA and componentB and render them? All of the lines that are commented out break the code.

Here's the codesandbox for this code.

CodePudding user response:

You can either pass a component instance and then clone the element. But take care here: if you pass a new property, which the component doesn't know, it wont be passed. So if you want to pass "anotherProp", then your Dummy component needs it as prop as well.

Or you can pass a component without calling it. Then inside another component you can "instantiate" it by familiar, i.e.: <ComponentB anotherProp="no" />

Please look at the following:

export default function App() {
  return <CompositeComponent componentA={<Dummy />} componentB={Dummy} />;
}

const Dummy = ({ answer }) => {
  return <div>Dummy: {answer}</div>;
};

const CompositeComponent = ({ componentA, componentB }) => {
  const ComponentB = componentB;
  return (
    <>
      {React.cloneElement(componentA, { answer: "yes" })}
      <ComponentB answer="no" />
    </>
  );
};

https://codesandbox.io/s/hungry-orla-0kt9ip

CodePudding user response:

this is how i got all six to work:

const ComponentA = componentA;
const ClonedA = React.cloneElement(componentA, { anotherProp: "yes" });
const CreatedA = React.createElement(componentA.type , { anotherProp: "yes" });
const ComponentB = componentB;
const ClonedB = React.cloneElement(<ComponentB />, { anotherProp: "yes" });
const CreatedB = React.createElement(componentB, { anotherProp: "yes" });
return (
  <div>
    { ComponentA }
    { ClonedA }
    { CreatedA }
    <ComponentB />
    { ClonedB }
    { CreatedB }
  </div>
);

must return these variables like { variable } because these hold elements, but aren't actually elements.

What is React.cloneElement()?

The React. cloneElement() function returns a copy of a specified element. Additional props and children can be passed on in the function. You would use this function when a parent component wants to add or modify the prop(s) of its children.

Why does my example of cloneElement() work?

My cloneElement() Code:

// Component A
const ClonedA = React.cloneElement(componentA, { anotherProp: "yes" });

// Component B
const ClonedB = React.cloneElement(<ComponentB />, { anotherProp: "yes" });

The main thing you were missing was wrapping component B as an element, since it was passed as an element this is what you need to do specify the element to react so it can clone it correctly. comp A was originally done correctly, just not inserted into the return correctly.

What is React.createElement()?

React. createElement( type, [props], [... children] ) Create and return a new React element of the given type. The type argument can be either a tag name string (such as 'div' or 'span' ), a React component type (a class or a function), or a React fragment type.

Why does my example of createElement() work?

My createElement() Code:

// Component A
const CreatedA = React.createElement(componentA.type , { anotherProp: "yes" });

// Component B
const CreatedB = React.createElement(componentB, { anotherProp: "yes" });

The main thing you were missing was specifying a type when creating your react elements. My fix to this was to add the .type to componentA to get it's type so it would render correctly. Your B component was done correctly since the props was already passed a component react was able to get the type with just the prop, component B was just not inserted into the return correctly.

Sandbox Code Here
  • Related