Search code examples
reactjscomponentscloneelement

React.CloneElement returning an object instead of function


I am having a hard time understanding the behaviour of React.cloneElement() function

I have my Component Structure something like this

A.js

export default class A extends React.Component {
     render() {
         return (<h1>{ this.props.message }</h1>)
     }
 }

B.js

import A from "./A"

const newComponent = React.cloneElement(A,{
    message: "Hello World"
})

export default newComponent

C.js

import B from "./B"
import { BrowserRouter as Router, Route } from "react-router-dom"

// To Be very precise
 export default class C extends React.Component {
     render() {
         return (
             <Router>
                <Route path="/" component={B}  />
            </Router>
         )
     }
 }

But I get this Error

Invalid prop component of type object supplied to Route, expected function.

but When I pass Component A directly into the Route component, it renders fine.

When I console.log Component A inside the render function of Component C, I get a function but When I console.log Component B inside the render function of Component C, I get a object

What am I missing?


Solution

  • First you need to understand the difference between React component and React element.Both are actually different.

    To be specific in jsx, in your case, A is a react component and <A /> is a react element. If you look at the React.cloneElement docs, then it expect an element as a first argument, but here you are passing a component. So first change you need to do is to pass an element to React.cloneElement like this

    const newComponent = React.cloneElement(<A />,{
        message: "Hello World"
    })
    

    The second thing is that the Route component expects a react component as component prop, but React.cloneElement returns a react element instead of component (that means newComponent is an element, not component). Therefore you cannot just simply export newComponent from the B.js file. You have to export a component instead. For that you can create a class component/stateless component. So your B.js should look something like this

    // B.js
    import A from "./A"
    
    const newComponent = React.cloneElement(<A />, {
      message: "Hello World"
    })
    
    export default class B extends React.Component {
      render() {
        return (<div>{newComponent}</div>)
      }
    }
    

    By the way, you don't even need cloneElement here in your case. You can simply return a component from B.js which render A. This is just for understanding purpose.