Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warn when user has multiple plugins implementing APIs that should only have 1 #2005

Closed
KyleAMathews opened this issue Sep 2, 2017 · 10 comments
Labels
type: documentation An issue or pull request for improving or updating Gatsby's documentation

Comments

@KyleAMathews
Copy link
Contributor

A community member asked on Discord why their Redux code wasn't working. After looking at their site for a bit I noticed they were also using gatsby-plugin-styled-components. Both their redux plugin & styled components implemented in their gatsby-ssr.js the API replaceRenderer—which there can only be one of.

We should when loading plugins check for cases like this and print out a warning in the console that things won't work as expected and suggest workarounds e.g. in this case, pull the styled-component SSR code into their custom redux code.

@akadop
Copy link
Contributor

akadop commented Sep 3, 2017

After combining the two replaceRenderer files, (removing the styled-components plugin , adding the code to the redux ssr code) ... I ended up with this file

import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

import { Provider } from 'react-redux'
import React from 'react'
import initStore from './src/shared/store/initRedux'
import { renderToString } from 'react-dom/server'

exports.replaceRenderer = ({
  bodyComponent,
  replaceBodyHTMLString,
  setHeadComponents
}) => {
  const sheet = new ServerStyleSheet()
  const store = initStore()

  const app = () => (
    <Provider store={store}>
      <StyleSheetManager sheet={sheet.instance}>
        {bodyComponent}
      </StyleSheetManager>
    </Provider>
  )

  const body = renderToString(app)

  replaceBodyHTMLString(body)

  setHeadComponents([sheet.getStyleElement()])

  return
}

by all means it looks like it should have worked, but instead I got anywhere between 12-24 errors along the lines of:

component---src-layouts-index-js-36c5da770483ba68dacf.js Failed to load resource: the server responded with a status of 404 (Not Found)
component---src-pages-index-js-87d1c17ad13dae91a1d2.js Failed to load resource: the server responded with a status of 404 (Not Found)
path---index-a0e39f21c11f6a62c5ab.js Failed to load resource: the server responded with a status of 404 (Not Found)
component---src-layouts-index-js-36c5da770483ba68dacf.js Failed to load resource: the server responded with a status of 404 (Not Found)
component---src-pages-index-js-87d1c17ad13dae91a1d2.js Failed to load resource: the server responded with a status of 404 (Not Found)
path---index-a0e39f21c11f6a62c5ab.js Failed to load resource: the server responded with a status of 404 (Not Found)
component---src-pages-index-js-87d1c17ad13dae91a1d2.js Failed to load resource: the server responded with a status of 404 (Not Found)
path---index-a0e39f21c11f6a62c5ab.js Failed to load resource: the server responded with a status of 404 (Not Found)
component---src-layouts-index-js-36c5da770483ba68dacf.js Failed to load resource: the server responded with a status of 404 (Not Found)
app-47a21a6fc641308aca59.js:4142 bundle loading error true
app-47a21a6fc641308aca59.js:933 Loading the component for / failed
app-47a21a6fc641308aca59.js:537 bundle loading error true
app-47a21a6fc641308aca59.js:940 Loading the JSON for / failed
app-47a21a6fc641308aca59.js:600 bundle loading error true
app-47a21a6fc641308aca59.js:948 Loading the Layout for / failed
component---src-pages-index-js-87d1c17ad13dae91a1d2.js Failed to load resource: the server responded with a status of 404 (Not Found)
path---index-a0e39f21c11f6a62c5ab.js Failed to load resource: the server responded with a status of 404 (Not Found)
component---src-layouts-index-js-36c5da770483ba68dacf.js Failed to load resource: the server responded with a status of 404 (Not Found)

Running gatsby build:

Minified React error #46; visit http://facebook.github.io/react  /docs/error-decoder.html?invariant=46 for the full message or use the non-minified dev environment for   full errors and additional helpful warnings.

    - render-page.js:4936 reactProdInvariant
      render-page.js:4936:16

    - render-page.js:19988 renderToString
      render-page.js:19988:122

    - render-page.js:42261 Object.exports.replaceRenderer
      render-page.js:42261:42

    - render-page.js:40928
      render-page.js:40928:39

    - render-page.js:40926 module.exports
      render-page.js:40926:26

    - render-page.js:176 module.exports
      render-page.js:176:31

@akadop
Copy link
Contributor

akadop commented Sep 3, 2017

Whoops, this appears to work only in development mode:

import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

import { Provider } from 'react-redux'
import React from 'react'
import initStore from './src/shared/store/initRedux'
import { renderToString } from 'react-dom/server'

exports.replaceRenderer = ({
  bodyComponent,
  replaceBodyHTMLString,
  setHeadComponents
}) => {
  const sheet = new ServerStyleSheet()
  const store = initStore()

  const app = () => (
    <Provider store={store}>
      <StyleSheetManager sheet={sheet.instance}>
        {bodyComponent}
      </StyleSheetManager>
    </Provider>
  )

  const body = renderToString(app)

  replaceBodyHTMLString(body)

  setHeadComponents([sheet.getStyleElement()])
}

You will get react minified error 46, which is "renderToString: you must pass a valid React Element"

so I got rid of the consts and can confirm this is working in both prod / dev

import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

import { Provider } from 'react-redux'
import React from 'react'
import initStore from './src/shared/store/initRedux'
import { renderToString } from 'react-dom/server'

exports.replaceRenderer = ({
  bodyComponent,
  replaceBodyHTMLString,
  setHeadComponents
}) => {
  const sheet = new ServerStyleSheet()
  const store = initStore()

  replaceBodyHTMLString(
    renderToString(
      <Provider store={store}>
        <StyleSheetManager sheet={sheet.instance}>
          {bodyComponent}
        </StyleSheetManager>
      </Provider>
    )
  )

  setHeadComponents([sheet.getStyleElement()])
}

@sebastienfi sebastienfi added API/Plugins type: documentation An issue or pull request for improving or updating Gatsby's documentation labels Oct 1, 2017
@casprwang
Copy link
Contributor

@akadop Thanks for the workaround! It saved me tons of time!

@monsieurnebo
Copy link
Contributor

Thank's a lot @akadop, I didn't understand why it wasn't working on SSR... You're the real mvp 👊

@m-allanson
Copy link
Contributor

This was done in #3889

@prajapati-parth
Copy link
Contributor

@akadop It's really strange how removing those constants makes this work. Any specific explanation for it.

@akadop
Copy link
Contributor

akadop commented Sep 1, 2018

@prajapati-parth
i presume it's because setting it as a const is making it a function that returns a react element, and the renderToString function does not take a function as a valid arg

@akadop
Copy link
Contributor

akadop commented Sep 1, 2018

@prajapati-parth

just checked renderToString()

export function renderToString(element: ReactElement<any>): string;

this is flow typed, so passing in an arg that isn't a ReactElement<any> will throw an error. A function returning the component will not meet the criteria.

this would probably work though:

const app = (
    <Provider store={store}>
      <StyleSheetManager sheet={sheet.instance}>
        {bodyComponent}
      </StyleSheetManager>
    </Provider>
  )

  const body = renderToString(app)

  replaceBodyHTMLString(body)

@prajapati-parth
Copy link
Contributor

@akadop
That makes sense. Thanks for digging it out.
We actually had got it wrong in the first place itself. I think since renderToString expects an element and since app in your example (#2005 (comment)) can be treated as a stateless functional component, replacing

const body = renderToString(app)

with

const body = renderToString(<app />)

would also work.
The modified example would look like

import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

import { Provider } from 'react-redux'
import React from 'react'
import initStore from './src/shared/store/initRedux'
import { renderToString } from 'react-dom/server'

exports.replaceRenderer = ({
  bodyComponent,
  replaceBodyHTMLString,
  setHeadComponents
}) => {
  const sheet = new ServerStyleSheet()
  const store = initStore()

  const app = () => (
    <Provider store={store}>
      <StyleSheetManager sheet={sheet.instance}>
        {bodyComponent}
      </StyleSheetManager>
    </Provider>
  )

  const body = renderToString(<app />)

  replaceBodyHTMLString(body)

  setHeadComponents([sheet.getStyleElement()])
}

@akadop
Copy link
Contributor

akadop commented Sep 1, 2018

yup, that works too! im not sure about performance implications though, since your wrapping it in another react component (even though it's an SFC.. same thing happens internally)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: documentation An issue or pull request for improving or updating Gatsby's documentation
Projects
None yet
Development

No branches or pull requests

7 participants