Gatsby: [V2] Make use of new React Context API ?

Created on 2 Jul 2018  路  8Comments  路  Source: gatsbyjs/gatsby

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 ?


Related to https://github.com/facebook/react/issues/13138

question or discussion

Most helpful comment

// 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?

All 8 comments

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.

Context Provider

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:

gatsby-browser.js

export const replaceRouterComponent = ({ history }) => {

  const ConnectedRouterWrapper = ({ children }) => (
    <ContextProvider>
      <Router history={history}>
        {children}
      </Router>
    </ContextProvider>
  );

  return ConnectedRouterWrapper;
}

gatsby-ssr.js

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()]);
}

Context Consumer

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>
        );
      }

    }

  );

}

Usage example

@connectToContext("isMenuOpen", "language")
export default class PageLayout extends React.PureComponent {
// PageLayout can now access to `this.props.isMenuOpen` and `this.props.language`

The issue

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.

gatsby-ssr.js

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mikestopcontinues picture mikestopcontinues  路  3Comments

brandonmp picture brandonmp  路  3Comments

magicly picture magicly  路  3Comments

rossPatton picture rossPatton  路  3Comments

Oppenheimer1 picture Oppenheimer1  路  3Comments