React: Change component's children from the outside












0















In a React project, I have a component Header.js, which is part of the global layout and is used on every page. Routing is done with React Router v4.



The Header.js component should have different children (like breadcrumbs or titles), depending on the page I'm currently on.



Example: on the HomePage.js, I want to display breadcrumbs. But on the AboutPage.js, I want to have just a title.



<App>
<Header />

<AnotherComponent />
<MoreComponents />
<BrowserRouter>
...
</BrowserRouter>
</App>


I thought about making Header.js a HOC, but I can't embed it onto every page because of layout and styling reasons. It needs to be in App.js in the exact same position.



Any ideas / techniques?



EDIT: Ideally, I want to have the content of Header.js in every page, like the Helmet package does it:



<>
{/* HomePage.js */}
<HeaderPortal>
<p>Content for the header</p>
</HeaderPortal>

<PageContent />
</>









share|improve this question


















  • 2





    You can nest routes that renders the component you want inside the <Header />, if some of them needs to address multiple routes you can match it against a regex

    – Sagiv b.g
    Jan 18 at 12:47











  • Yeah, I thought about the same, but I would like to keep the <Header /> clean and not put all the content inside it. The content that is going to be displayed in <Header /> should come from the page components.

    – Andreas Remdt
    Jan 18 at 13:28











  • If you render children then the App can nest those routes. Or maybe use react portals (which i think is less preferred for this use case)

    – Sagiv b.g
    Jan 18 at 13:34
















0















In a React project, I have a component Header.js, which is part of the global layout and is used on every page. Routing is done with React Router v4.



The Header.js component should have different children (like breadcrumbs or titles), depending on the page I'm currently on.



Example: on the HomePage.js, I want to display breadcrumbs. But on the AboutPage.js, I want to have just a title.



<App>
<Header />

<AnotherComponent />
<MoreComponents />
<BrowserRouter>
...
</BrowserRouter>
</App>


I thought about making Header.js a HOC, but I can't embed it onto every page because of layout and styling reasons. It needs to be in App.js in the exact same position.



Any ideas / techniques?



EDIT: Ideally, I want to have the content of Header.js in every page, like the Helmet package does it:



<>
{/* HomePage.js */}
<HeaderPortal>
<p>Content for the header</p>
</HeaderPortal>

<PageContent />
</>









share|improve this question


















  • 2





    You can nest routes that renders the component you want inside the <Header />, if some of them needs to address multiple routes you can match it against a regex

    – Sagiv b.g
    Jan 18 at 12:47











  • Yeah, I thought about the same, but I would like to keep the <Header /> clean and not put all the content inside it. The content that is going to be displayed in <Header /> should come from the page components.

    – Andreas Remdt
    Jan 18 at 13:28











  • If you render children then the App can nest those routes. Or maybe use react portals (which i think is less preferred for this use case)

    – Sagiv b.g
    Jan 18 at 13:34














0












0








0








In a React project, I have a component Header.js, which is part of the global layout and is used on every page. Routing is done with React Router v4.



The Header.js component should have different children (like breadcrumbs or titles), depending on the page I'm currently on.



Example: on the HomePage.js, I want to display breadcrumbs. But on the AboutPage.js, I want to have just a title.



<App>
<Header />

<AnotherComponent />
<MoreComponents />
<BrowserRouter>
...
</BrowserRouter>
</App>


I thought about making Header.js a HOC, but I can't embed it onto every page because of layout and styling reasons. It needs to be in App.js in the exact same position.



Any ideas / techniques?



EDIT: Ideally, I want to have the content of Header.js in every page, like the Helmet package does it:



<>
{/* HomePage.js */}
<HeaderPortal>
<p>Content for the header</p>
</HeaderPortal>

<PageContent />
</>









share|improve this question














In a React project, I have a component Header.js, which is part of the global layout and is used on every page. Routing is done with React Router v4.



The Header.js component should have different children (like breadcrumbs or titles), depending on the page I'm currently on.



Example: on the HomePage.js, I want to display breadcrumbs. But on the AboutPage.js, I want to have just a title.



<App>
<Header />

<AnotherComponent />
<MoreComponents />
<BrowserRouter>
...
</BrowserRouter>
</App>


I thought about making Header.js a HOC, but I can't embed it onto every page because of layout and styling reasons. It needs to be in App.js in the exact same position.



Any ideas / techniques?



EDIT: Ideally, I want to have the content of Header.js in every page, like the Helmet package does it:



<>
{/* HomePage.js */}
<HeaderPortal>
<p>Content for the header</p>
</HeaderPortal>

<PageContent />
</>






javascript reactjs






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Jan 18 at 12:41









Andreas RemdtAndreas Remdt

2015




2015








  • 2





    You can nest routes that renders the component you want inside the <Header />, if some of them needs to address multiple routes you can match it against a regex

    – Sagiv b.g
    Jan 18 at 12:47











  • Yeah, I thought about the same, but I would like to keep the <Header /> clean and not put all the content inside it. The content that is going to be displayed in <Header /> should come from the page components.

    – Andreas Remdt
    Jan 18 at 13:28











  • If you render children then the App can nest those routes. Or maybe use react portals (which i think is less preferred for this use case)

    – Sagiv b.g
    Jan 18 at 13:34














  • 2





    You can nest routes that renders the component you want inside the <Header />, if some of them needs to address multiple routes you can match it against a regex

    – Sagiv b.g
    Jan 18 at 12:47











  • Yeah, I thought about the same, but I would like to keep the <Header /> clean and not put all the content inside it. The content that is going to be displayed in <Header /> should come from the page components.

    – Andreas Remdt
    Jan 18 at 13:28











  • If you render children then the App can nest those routes. Or maybe use react portals (which i think is less preferred for this use case)

    – Sagiv b.g
    Jan 18 at 13:34








2




2





You can nest routes that renders the component you want inside the <Header />, if some of them needs to address multiple routes you can match it against a regex

– Sagiv b.g
Jan 18 at 12:47





You can nest routes that renders the component you want inside the <Header />, if some of them needs to address multiple routes you can match it against a regex

– Sagiv b.g
Jan 18 at 12:47













Yeah, I thought about the same, but I would like to keep the <Header /> clean and not put all the content inside it. The content that is going to be displayed in <Header /> should come from the page components.

– Andreas Remdt
Jan 18 at 13:28





Yeah, I thought about the same, but I would like to keep the <Header /> clean and not put all the content inside it. The content that is going to be displayed in <Header /> should come from the page components.

– Andreas Remdt
Jan 18 at 13:28













If you render children then the App can nest those routes. Or maybe use react portals (which i think is less preferred for this use case)

– Sagiv b.g
Jan 18 at 13:34





If you render children then the App can nest those routes. Or maybe use react portals (which i think is less preferred for this use case)

– Sagiv b.g
Jan 18 at 13:34












1 Answer
1






active

oldest

votes


















0














One way to do this is by using React's context API, but you'll have to wrap pretty high in the DOM to be able to provide the setters and getters of the header content from wherever.



The Header components would look like this:



const HeaderContext = React.createContext();

// Provides the context and state - wrap app
export class HeaderProvider extends React.Component {
state = {};
render() {
const { children } = this.props;
return (
<HeaderContext.Provider
value={{
header: this.state.header,
setHeader: ({ header }) => this.setState({ header })
}}
>
{children}
</HeaderContext.Provider>
);
}
}
// Wrapper around WHERE the header will be rendered
export function HeaderPlacement() {
return (
<HeaderContext.Consumer>{({ header }) => header}</HeaderContext.Consumer>
);
}
// Wrapper for setting the header CONTENT
export function HeaderPortal({ children }) {
return (
<HeaderContext.Consumer>
{({ header, setHeader }) =>
!header // Guard against setting the header in an endless loop
&& setHeader({ header: children })}
</HeaderContext.Consumer>
);
}


And then the usage would look like this:



function App() {
return (
<HeaderProvider>
<div className="App">
<HeaderPlacement />
<Page />
</div>
</HeaderProvider>
);
}

function Page() {
return (
<React.Fragment>
<HeaderPortal><h1>Page A Test</h1></HeaderPortal>
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ex odio,
eleifend in consequat nec, maximus lobortis ligula. Duis elementum, diam
et imperdiet pulvinar, nulla ligula commodo elit, ac ullamcorper justo
mauris quis velit.
</div>
</React.Fragment>
);
}


You'll have to be careful if multiple descendants try setting the header content, because you will either end up with a looping setState issue, or one will overwrite the other.



This is definitely not the only way, and I avoided using hooks, but this is certainly a React-y way.



Checkout the codesandbox here: https://codesandbox.io/s/pj2q10700j






share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54254273%2freact-change-components-children-from-the-outside%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    One way to do this is by using React's context API, but you'll have to wrap pretty high in the DOM to be able to provide the setters and getters of the header content from wherever.



    The Header components would look like this:



    const HeaderContext = React.createContext();

    // Provides the context and state - wrap app
    export class HeaderProvider extends React.Component {
    state = {};
    render() {
    const { children } = this.props;
    return (
    <HeaderContext.Provider
    value={{
    header: this.state.header,
    setHeader: ({ header }) => this.setState({ header })
    }}
    >
    {children}
    </HeaderContext.Provider>
    );
    }
    }
    // Wrapper around WHERE the header will be rendered
    export function HeaderPlacement() {
    return (
    <HeaderContext.Consumer>{({ header }) => header}</HeaderContext.Consumer>
    );
    }
    // Wrapper for setting the header CONTENT
    export function HeaderPortal({ children }) {
    return (
    <HeaderContext.Consumer>
    {({ header, setHeader }) =>
    !header // Guard against setting the header in an endless loop
    && setHeader({ header: children })}
    </HeaderContext.Consumer>
    );
    }


    And then the usage would look like this:



    function App() {
    return (
    <HeaderProvider>
    <div className="App">
    <HeaderPlacement />
    <Page />
    </div>
    </HeaderProvider>
    );
    }

    function Page() {
    return (
    <React.Fragment>
    <HeaderPortal><h1>Page A Test</h1></HeaderPortal>
    <div>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ex odio,
    eleifend in consequat nec, maximus lobortis ligula. Duis elementum, diam
    et imperdiet pulvinar, nulla ligula commodo elit, ac ullamcorper justo
    mauris quis velit.
    </div>
    </React.Fragment>
    );
    }


    You'll have to be careful if multiple descendants try setting the header content, because you will either end up with a looping setState issue, or one will overwrite the other.



    This is definitely not the only way, and I avoided using hooks, but this is certainly a React-y way.



    Checkout the codesandbox here: https://codesandbox.io/s/pj2q10700j






    share|improve this answer




























      0














      One way to do this is by using React's context API, but you'll have to wrap pretty high in the DOM to be able to provide the setters and getters of the header content from wherever.



      The Header components would look like this:



      const HeaderContext = React.createContext();

      // Provides the context and state - wrap app
      export class HeaderProvider extends React.Component {
      state = {};
      render() {
      const { children } = this.props;
      return (
      <HeaderContext.Provider
      value={{
      header: this.state.header,
      setHeader: ({ header }) => this.setState({ header })
      }}
      >
      {children}
      </HeaderContext.Provider>
      );
      }
      }
      // Wrapper around WHERE the header will be rendered
      export function HeaderPlacement() {
      return (
      <HeaderContext.Consumer>{({ header }) => header}</HeaderContext.Consumer>
      );
      }
      // Wrapper for setting the header CONTENT
      export function HeaderPortal({ children }) {
      return (
      <HeaderContext.Consumer>
      {({ header, setHeader }) =>
      !header // Guard against setting the header in an endless loop
      && setHeader({ header: children })}
      </HeaderContext.Consumer>
      );
      }


      And then the usage would look like this:



      function App() {
      return (
      <HeaderProvider>
      <div className="App">
      <HeaderPlacement />
      <Page />
      </div>
      </HeaderProvider>
      );
      }

      function Page() {
      return (
      <React.Fragment>
      <HeaderPortal><h1>Page A Test</h1></HeaderPortal>
      <div>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ex odio,
      eleifend in consequat nec, maximus lobortis ligula. Duis elementum, diam
      et imperdiet pulvinar, nulla ligula commodo elit, ac ullamcorper justo
      mauris quis velit.
      </div>
      </React.Fragment>
      );
      }


      You'll have to be careful if multiple descendants try setting the header content, because you will either end up with a looping setState issue, or one will overwrite the other.



      This is definitely not the only way, and I avoided using hooks, but this is certainly a React-y way.



      Checkout the codesandbox here: https://codesandbox.io/s/pj2q10700j






      share|improve this answer


























        0












        0








        0







        One way to do this is by using React's context API, but you'll have to wrap pretty high in the DOM to be able to provide the setters and getters of the header content from wherever.



        The Header components would look like this:



        const HeaderContext = React.createContext();

        // Provides the context and state - wrap app
        export class HeaderProvider extends React.Component {
        state = {};
        render() {
        const { children } = this.props;
        return (
        <HeaderContext.Provider
        value={{
        header: this.state.header,
        setHeader: ({ header }) => this.setState({ header })
        }}
        >
        {children}
        </HeaderContext.Provider>
        );
        }
        }
        // Wrapper around WHERE the header will be rendered
        export function HeaderPlacement() {
        return (
        <HeaderContext.Consumer>{({ header }) => header}</HeaderContext.Consumer>
        );
        }
        // Wrapper for setting the header CONTENT
        export function HeaderPortal({ children }) {
        return (
        <HeaderContext.Consumer>
        {({ header, setHeader }) =>
        !header // Guard against setting the header in an endless loop
        && setHeader({ header: children })}
        </HeaderContext.Consumer>
        );
        }


        And then the usage would look like this:



        function App() {
        return (
        <HeaderProvider>
        <div className="App">
        <HeaderPlacement />
        <Page />
        </div>
        </HeaderProvider>
        );
        }

        function Page() {
        return (
        <React.Fragment>
        <HeaderPortal><h1>Page A Test</h1></HeaderPortal>
        <div>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ex odio,
        eleifend in consequat nec, maximus lobortis ligula. Duis elementum, diam
        et imperdiet pulvinar, nulla ligula commodo elit, ac ullamcorper justo
        mauris quis velit.
        </div>
        </React.Fragment>
        );
        }


        You'll have to be careful if multiple descendants try setting the header content, because you will either end up with a looping setState issue, or one will overwrite the other.



        This is definitely not the only way, and I avoided using hooks, but this is certainly a React-y way.



        Checkout the codesandbox here: https://codesandbox.io/s/pj2q10700j






        share|improve this answer













        One way to do this is by using React's context API, but you'll have to wrap pretty high in the DOM to be able to provide the setters and getters of the header content from wherever.



        The Header components would look like this:



        const HeaderContext = React.createContext();

        // Provides the context and state - wrap app
        export class HeaderProvider extends React.Component {
        state = {};
        render() {
        const { children } = this.props;
        return (
        <HeaderContext.Provider
        value={{
        header: this.state.header,
        setHeader: ({ header }) => this.setState({ header })
        }}
        >
        {children}
        </HeaderContext.Provider>
        );
        }
        }
        // Wrapper around WHERE the header will be rendered
        export function HeaderPlacement() {
        return (
        <HeaderContext.Consumer>{({ header }) => header}</HeaderContext.Consumer>
        );
        }
        // Wrapper for setting the header CONTENT
        export function HeaderPortal({ children }) {
        return (
        <HeaderContext.Consumer>
        {({ header, setHeader }) =>
        !header // Guard against setting the header in an endless loop
        && setHeader({ header: children })}
        </HeaderContext.Consumer>
        );
        }


        And then the usage would look like this:



        function App() {
        return (
        <HeaderProvider>
        <div className="App">
        <HeaderPlacement />
        <Page />
        </div>
        </HeaderProvider>
        );
        }

        function Page() {
        return (
        <React.Fragment>
        <HeaderPortal><h1>Page A Test</h1></HeaderPortal>
        <div>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ex odio,
        eleifend in consequat nec, maximus lobortis ligula. Duis elementum, diam
        et imperdiet pulvinar, nulla ligula commodo elit, ac ullamcorper justo
        mauris quis velit.
        </div>
        </React.Fragment>
        );
        }


        You'll have to be careful if multiple descendants try setting the header content, because you will either end up with a looping setState issue, or one will overwrite the other.



        This is definitely not the only way, and I avoided using hooks, but this is certainly a React-y way.



        Checkout the codesandbox here: https://codesandbox.io/s/pj2q10700j







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered 15 hours ago









        Matty JMatty J

        716




        716






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54254273%2freact-change-components-children-from-the-outside%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Liquibase includeAll doesn't find base path

            How to use setInterval in EJS file?

            Petrus Granier-Deferre