Search code examples
javascriptreactjsreact-router-dom

The file that's calling props.children is not rendering the content in the parent


Default.HOC.js

import React from "react";
import { Route, Routes } from "react-router-dom";
import DefaultLayout from "../layouts/Default.layout";

const DefaultHOC = ({ component: Component, ...rest }) => {
  // component
  // props -> path exact
  return (
    <Routes>
      <Route
        {...rest}
        component = {(props) => {
          <DefaultLayout>
            <Component {...props} />
          </DefaultLayout>
        }}
      />
    </Routes>
  );
};

export default DefaultHOC;

temp.js

import React from "react";

function Temp() {
  return <h1>This is a temp component</h1>
}

export default Temp;

Default.layout.js

import React from "react";

const DefaultLayout = (props) => {
  return (
    <div style={{ padding: '10px' }}>
      <h1>Default Layout Page</h1>
      {props.children}
    </div>
  );
};

export default DefaultLayout; 

App.js

import { Route, Routes } from "react-router-dom";

// HOC
import DefaultHOC from "./HOC/Default.HOC";

// Component
import Temp from "./components/temp";

function App() {
  return (
    <>
      <DefaultHOC path="/" exact element={<Temp />} />
    </>
  );
}

export default App;

The video lecture I'm following has used the older version, the problem is that when I'm calling children, only the Temp is being rendered, i.e, the screen only shows:

"This is a temp component"

whereas it should show the result as:

"Default Layout Page This is a temp component"

I've tried replacing component with element, it shows the same result. Also, when I remove the destructured props and all, it only shows "Default layout page".


Solution

  • Issues

    1. DefaultHOC is misnamed, it's not a Higher Order Component, e.g. a function component that returns a function component, it's just a regular React function component.
    2. DefaultHOC needs to render the Route correctly, e.g. passing the correct props. There is no component prop, there exists only a single element prop that takes a React.ReactNode, e.g. JSX, as an argument.

    The current code only renders the <Temp /> component because it was passed to DefaultHOC on the element prop which was spread through to the Route component along with the path prop. The component prop is ignored.

    <DefaultHOC path="/" exact element={<Temp />} />
    
    const DefaultHOC = ({ component: Component, ...rest }) => {
      return (
        <Routes>
          <Route
            {...rest} // <-- path and element pass through here
            component={(props) => { // <-- extraneous ignored prop
              <DefaultLayout>
                <Component {...props} />
              </DefaultLayout>;
            }}
          />
        </Routes>
      );
    };
    

    Solution

    Refactor the DefaultHOC to render DefaultLayout and passed component prop on the Route component's element prop. I suggest also renaming the component since it's not a HOC.

    Example:

    const DefaultComponent = ({ component, ...props }) => {
      return (
        <Routes>
          <Route {...props} element={<DefaultLayout>{component}</DefaultLayout>} />
        </Routes>
      );
    };
    
    <DefaultComponent path="/" component={<Temp />} />
    

    Edit the-file-thats-calling-props-children-its-not-rendering-the-content-in-the-p

    enter image description here

    Suggested Solution

    The above appears to be a very unconventional method is sharing UI via a layout. The idiomatic way to render certain components into certain UIs in react-router@6 is to use Layout Routes. Layout routes render "common UI" and possibly house "common state/logic" and render an Outlet for nested routes to render their content into.

    Example:

    import { Outlet } from "react-router-dom";
    
    const DefaultLayout = () => {
      return (
        <div style={{ padding: "10px" }}>
          <h1> Default Layout Page </h1>
          <Outlet />
        </div>
      );
    };
    

    App.js

    import { Route, Routes } from "react-router-dom";
    
    ...
    
    export default function App() {
      return (
        <Routes>
          <Route element={<DefaultLayout />}>
            <Route path="/" element={<Temp />} />
          </Route>
        </Routes>
      );
    }
    

    Edit the-file-thats-calling-props-children-its-not-rendering-the-content-in-the-p (forked)

    enter image description here