React: Change component's children from the outside
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
add a comment |
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
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 renderchildren
then theApp
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
add a comment |
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
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
javascript reactjs
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 renderchildren
then theApp
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
add a comment |
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 renderchildren
then theApp
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
add a comment |
1 Answer
1
active
oldest
votes
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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
add a comment |
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
add a comment |
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
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
answered 15 hours ago
Matty JMatty J
716
716
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 theApp
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