javascriptreactjsreact-hooksreact-16

Difference between passing a ReactElement and a function that returns a ReactElement


Whats the difference between passing a ReactElement as a property:

First case

<RenderParam ReactElement={<Counter />} />

function RenderParam({ ReactElement }) {
  return <div>{ReactElement}</div>;
}

and passing a function that returns a ReactElement:

Second case

const instantiatedCounter = () => <Counter />; 

<RenderParam ReactElement={instantiatedCounter} />

function RenderParam({ ReactElement }) {
  return <div> <ReactElement /> </div> 
}

I see there are differences in the lifecycle:

ReactElement changed (at RenderParam lifecycle)
component did mount (at Counter)

I dont see whats difference between them. Why first case its able to keep its state?


Solution

  • The first example passes a static JSX element <Counter /> as RenderParam prop. The second example uses a function instantiatedCounter, that allows to return a more dynamic JSX element, commonly referred to as Render props.

    In the second case you lose state, because React treats the ReactElement prop as a freshly defined component every time, unmounts the old one and mounts it again on every render cycle. What you want to do is call the ReactElement prop to retrieve a JSX element as return value:

    function RenderParam({ ReactElement }) {
      return <div>{ReactElement()}</div>; 
      // not: return <div><ReactElement /></div>
    }
    

    You could as well define it with JSX syntax <div><ReactElement /></div>. But then make sure, instantiatedCounter is a static function and not re-created on every render, so the object reference stays same:

    const instantiatedCounter = () => <Counter />; 
    // make it static in some way, so object reference doesn't change
    
    export default function App() {
      // .. and remove instantiatedCounter here
      return <RenderParam ReactElement={instantiatedCounter} />
    }