Hi guys,
I'm using Redux do manage some shared state values (e.g. isMenuOpen, isModalOpen, ...) that are used by multiple components across the React DOM tree.
With the new React Context API being available through Gatsby V2, I'm wondering if this could be used in the purpose of replacing Redux?
I already made some tests that are successful in runtime, but this new Context doesn't seem to support SSR.... (it's crashing during buildtime).
Did any of you used it ? Is that possible ?
Using createContext with Provider/Consumer works for me on a Gatsby v2 setup, also on build. How exactly are you using it?
Here we go:
The Context is used as a Redux replacement, to manage some globally shared values (such as isMenuOpen) across the webapp.
I have a <ContextProvider /> component:
// ...
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
);
}
That is used both in gatsby-browser.js and gatsby-ssr.js:
export const replaceRouterComponent = ({ history }) => {
const ConnectedRouterWrapper = ({ children }) => (
<ContextProvider>
<Router history={history}>
{children}
</Router>
</ContextProvider>
);
return ConnectedRouterWrapper;
}
exports.replaceRenderer = ({
bodyComponent,
replaceBodyHTMLString,
setHeadComponents
}) => {
const sheet = new ServerStyleSheet();
const app = (
<ContextProvider>
<StyleSheetManager sheet={sheet.instance}>
{bodyComponent}
</StyleSheetManager>
</ContextProvider>
);
const body = renderToString(app);
replaceBodyHTMLString(body);
setHeadComponents([sheet.getStyleElement()]);
}
I have a HOC that let any component connect to the context and get the wanted values, via a decorator:
export default function connectToContext(...properties) {
return WrappedComponent => (
class extends React.PureComponent {
renderWrappedComponent(context) {
const { store } = context;
const contextProperties = properties.reduce((accu, property) => {
accu[property] = context[property];
return accu;
}, {});
return <WrappedComponent {...this.props} {...contextProperties} />;
}
render() {
return (
<Context.Consumer>
{context => this.renderWrappedComponent(context)}
</Context.Consumer>
);
}
}
);
}
@connectToContext("isMenuOpen", "language")
export default class PageLayout extends React.PureComponent {
// PageLayout can now access to `this.props.isMenuOpen` and `this.props.language`
It's working fine via gatsby develop, but the gatsby build is crashing with the following error:
```error Building static HTML for pages failed
See our docs page on debugging HTML builds for help https://goo.gl/yL9lND
15 | renderWrappedComponent(context) {
16 | const { store } = context;
| ^
17 | const contextProperties = properties.reduce((accu, property) => {
18 |
WebpackError: TypeError: Cannot destructure property store of 'undefined' or 'null'.
```
=> The Context seems to be undefined / null. Any idea of the cause?
@fabe I would be interested to see how you're using it.
Can you create minimal repository to reproduce it? I didn't try using Context but it seems it works for @fabe so possibly this might be usage problem here
Hi @pieh ,
I just created a basic example that reproduces this "bug".
The React Context stores a isPowerOn value (boolean) that can be updated via the togglePower method (also from the context).
It's working fine via gatsby develop, but not during a gatsby build:
14 | renderWrappedComponent(context) {
15 | const contextProperties = properties.reduce((accu, property) => {
> 16 | accu[property] = context[property];
| ^
17 | return accu;
18 | }, {});
19 |
WebpackError: TypeError: Cannot read property 'isPowerOn' of undefined
My setup with React Context + Gatsby is very similar, not sure why context is undefined during build there. The HOC doesn't seem to be the issue. You can check out my implementation here: https://github.com/fabe/gatsby-universal
Ahum.
import React from "react";
import { renderToString } from "react-dom/server";
import ContextProvider from "./src/context";
- exports.replaceRenderer = ({
+ export const replaceRenderer = ({
bodyComponent,
replaceBodyHTMLString,
setHeadComponents
}) => {
- const app = (
+ const App = () => {
+ return (
<ContextProvider>
{bodyComponent}
</ContextProvider>
);
+ };
const body = renderToString(app);
replaceBodyHTMLString(body);
}
馃檴馃檲馃檳
Probably be related to this v2 migration point.
Mixing import and module.exports is not ok:
// BAD: Mixed ES and CommonJS module syntax will cause failures
import foo from "foo"
module.exports = foo
Thanks @fabe , your repo made me search outside the React Context code... And find this.
// BAD: Mixed ES and CommonJS module syntax will cause failures
Maybe this should break the Gatsby build instead of silently leading to weird edge cases?
Maybe this should break the Gatsby build instead of silently leading to weird edge cases?
馃憤 to that, Gatsby should be handling this in a more sensible way. It's also the cause of #4718, so should be fixed as part of resolving that issue.
Most helpful comment
Maybe this should break the Gatsby build instead of silently leading to weird edge cases?