menu

Gatsby.js

Fast in every way that matters. Gatsby is a free and open source framework based on React that helps developers build blazing fast websites and apps.

Channels
# All channels
view-forward
# General
view-forward
# I Made This
view-forward
# Meta
view-forward
# Themes
view-forward
Team

Passing props down to children from layout v2

February 17, 2020 at 4:52pm

Passing props down to children from layout v2

February 17, 2020 at 4:52pm
Hi there,
I've searched around and not found a concrete answer on the best way to do this in v2, it seems like it was straightforward in v1 but not so now.
In short, I have a layout file based on the gatsby-starter that wraps around children from it's props. In the layout, I set a Hook based state and I want to pass the setState function for it down to the children so the state can be changed by a component within the page.

Code:

Below I want to pass the setSelectedTheme function down to children.

Layout.js

const Layout = ({ children }) => {
const [selectedTheme, setSelectedTheme] = useState('light')
library.add(fab, faEnvelope)
return (
<ThemeProvider theme={selectedTheme === 'light' ? lightTheme : darkTheme}>
<GlobalStyles />
<Container>
<main>{children}</main> // **I want to pass *setSelectedTheme* to children here**
</Container>
</ThemeProvider>
)
}
Layout.propTypes = {
children: PropTypes.node.isRequired,
}
export default Layout

February 17, 2020 at 5:02pm
As far as I know, you can’t. The two workarounds I’ve found are 1) create a “wrapper” component underneath the layout that handles your top-level state, or 2) use the Context API. I try to use Context sparingly, so I use a wrapper component for as much as possible, and only use Context when it is absolutely necessary. Having a wrapper component also keeps the layout component nice and clean, that way layout only handles the top level structure.
like-fill
1
  • reply
  • like
I agree with the methods describe, and I’d probably recommend using Context, but it depends on some finer details in execution. I do want to add, however, that it is possible to change an Element’s arguments using React.CloneElement, but a better method than that might be to use functional children, or render props like that article suggests. Depending on how you compose your components, you could use render props with a type of HOC that basically does dependency injection by passing your Page component to the Layout component AS the render prop; so your Layout component would receive the Page component, and render it, passing it the setState() function as one of its required properties.
like-fill
1
  • reply
  • like
here’s an article talking about component injection. In that one they talk about Functional Children being an anti-pattern, because of “naming conventions”.... which I disagree with. The argument doesn’t really fly with me because you can simply alias Children to whatever meaningful name you need... same way they do later on with the render prop. I would have agreed if they had talked about how it contradicts the standard practice of passing an ARRAY of children. In that respect, it does make more sense to use a render prop, but not as much if you’re using Typescript, where you can create a binding, build time declaration of the shape of your children.
  • reply
  • like

February 19, 2020 at 10:26am
Thanks guys, shame it's not so simple now but appreciate the responses!
  • reply
  • like

February 20, 2020 at 6:07pm
Hey , we have done this in a pretty simple way:
const childrenWithProps = React.Children.map(children, child =>
React.cloneElement(child, {
foo: "bar",
// anything you want to pass down can go here
}),
);
And then in the render() method of your base file, instead of putting {children} you just put {childrenWithProps} and all children will be able to access the props you have set in the above code.
Edited
like-fill
1
  • reply
  • like

February 23, 2020 at 9:52am
Thanks for that Jack!
  • reply
  • like