My app is like this:
Gatsby project
index.tsx - Entry point of app - I call
<Provider store={store}>
<HomePage />
</Provider>
HomePage.js uses mapStateToProps
and uses connect(mapStateToProps)(HomePage)
Inside my HomePage, of course I use multiple React components. And I can make use of the store, read/write of anything that happens on that HomePage
.
On my Homepage, I have links to ActivityPage
. Once I click on this page and land there - problem starts.
ActivityPage makes use of multiple sub-components to achieve tasks. How do I access read/write from ActivityPage?
I am unable to useDispatch
to write or even read from the store. I can see the store on my ReactDev tools, but on trying to read/write I get 11 errors, mostly along the lines of I need to wrap Components with <Provider>
I read different solutions on Stack Overflow which suggested that I need to have mapStateToProps
and use connect
on whichever Component I need access the store.
This is not working, as I am specifically looking to get access to the store from a 'new page'. Do I need to go and make another store for my child pages? Help please.
:
Your component must be inside of a redux Provider
component in order to use the redux hooks useSelector
and useDispatch
or the connect
HOC.
In a typical React app you would place the Provider
in your App
component which is a parent of everything. However Gatsby uses a different setup where each page is totally independent, so there is no shared parent where we can place the Provider
.
What Gatsby does have is an API for overriding customizing behaviors by defining functions in configuration files. There are a bunch of files that you can place in the root of your app, in the same folder as package.json
and outside of src
. The two that we will use here are gatsby-browser.js
, which controls the client-side, and gatsby-ssr.js
, which controls the creation of static HTML pages through server-side rendering. Both of these files support a function called wrapRootElement
.
We use the wrapRootElement
function to place our Redux provider as a wrapper around every page. Since we are using the same function in two files, the official "using-redux" example defines that function in a separate file and imports it into both of the configurations. wrap-with-provider.js
is not a special file name, it's just a holder for the function.
A wrapRootElement
function receives the element
as a prop which is similar to the children
prop. Our function creates a store instance and returns a Provider
with that store which has the element
as its child. We are creating the store in this function, so if your current redux file is exporting a created store
as a constant, you'll want to export a callable function that creates the store instead. You can see their example here.
import React from "react"
import { Provider } from "react-redux"
import createStore from "./src/state/createStore"
// eslint-disable-next-line react/display-name,react/prop-types
export default ({ element }) => {
// Instantiating store in `wrapRootElement` handler ensures:
// - there is fresh store for each SSR page
// - it will be called only once in browser, when React mounts
const store = createStore()
return <Provider store={store}>{element}</Provider>
}
Again, this file does not manipulate Gatsby behavior on its own. We need to export its value and assign it to a special variable in a configuration file to do that. Here are those files:
import wrapWithProvider from "./wrap-with-provider"
export const wrapRootElement = wrapWithProvider
import wrapWithProvider from "./wrap-with-provider"
export const wrapRootElement = wrapWithProvider
The content is the same in both. All we are doing is importing the function that we created in wrap-with-provider.js
and assigning it to the special named variable wrapRootElement
which controls behavior.