Closed TheoGil closed 4 years ago
I have finally managed to pass extra data from the Layout to the page component using React Context. It turns out that everything that I needed was to dive into the documentation and some trial and error.
The following ressources gave me all the information that I needed to get this working:
I'm going to describe below the changes that I've made to my app to get this working, in case it could help someone in the future. To who it may concern, I'm not an expert in React or Gatsby or the use of Context, this might not be the most elegant way to do it but it works for my case. If you're in a rush or just like to copy paste code from strangers (😁), there you go!
// ./src/context/IntroContext.js
import React from "react"
// Note: I'm not passing a defaultValue to createContext // as the whole app is going to be wrapped into the IntroProvider using the browser API wrapRootElement const IntroContext = React.createContext()
class IntroProvider extends React.Component { state = { shouldPlayIntro: true, }
update = newState => { this.setState(newState) }
render() { return ( <IntroContext.Provider value={{ shouldPlayIntro: this.state.shouldPlayIntro, update: this.update, }}
{this.props.children} </IntroContext.Provider> ) } } export default IntroContext export { IntroProvider }
2. Use the `wrapRootElement` method of the Gatsby Browser API to wrap our whole app into the `IntroProvider`. This will allow any child component to access the `IntroContext`
```js
// ./gatsby-browser.js
import React from "react"
import { IntroProvider } from "./src/context/IntroContext"
export function wrapRootElement({ element }) {
return <IntroProvider>{element}</IntroProvider>
}
IntroContext
is available to our components, we can read and update the data it holds. In this example, I'm in the Layout component but you can do it in every component (pages and so on)
// ./src/components/layout.js
import React from "react"
import IntroContext from "../context/IntroContext"
import Header from "./header" // unrelated
import ContactBadge from "./contact_badge/contact" // unrelated
export default class Layout extends React.Component { static contextType = IntroContext // lets you consume the nearest current value of that Context type using this.context
componentDidMount() { console.log(this.context.shouldPlayIntro) // true
setTimeout(() => {
this.context.update({ shouldPlayIntro: false })
console.log(this.context.shouldPlayIntro) // false
}, 2000)
}
render() { return (
)
} }
4. When in my page component, I listen to context changes by storing a reference to the previous state, and comparing the previous and new state in the `componentDidUpdate` method
```js
// ./src/pages/index.js
import React from "react"
import IntroContext from "../context/IntroContext"
export default class Index extends React.PureComponent {
static contextType = IntroContext
static previousContext
componentDidMount() {
this.previousContext = this.context
}
componentDidUpdate() {
if (this.previousContext.shouldPlayIntro !== this.context.shouldPlayIntro) {
this.previousContext = this.context
// the context shouldPlayIntro property has changed, do some stuff
}
}
}
This is more of a question/how to than a actual bug.
I'm using this plugin with the layout option and would like to send data from the state of the Layout component to the pages components.
Ultimately, what I want, is to perfom an "introduction animation" from Layout component, that will only run once upon the initial page load. This animation will move around the persistent elements that are located in Layout. Once this animation is complete, I want to animate in the page content.
Something like:
I can't figure out the proper way to do this though. I've managed to pass extra props to the page component but his break the page transitions, as my page component isn't wrapped in the Transitions components anymore...
I've also taken a look at React Context, as advised here (Passing data from Layout to Page is exactly what i want to achieve). But I do not see how I can apply this in my case, as I want to be able to read the data from the componentDidMount/componentDidUpdate methods of the page component
edit: I've tried to use this cleaner solution from this stack overflow post, this does not break the page transition, but unfortunately the additional intro prop is only passed to the TransitionHandler component and not its children (ie my page component).