prescottprue / react-redux-firebase

Redux bindings for Firebase. Includes React Hooks and Higher Order Components.
https://react-redux-firebase.com
MIT License
2.55k stars 559 forks source link

feat(core): Gatsby with ReactReduxFirebaseProvider and SSR #702

Open matchatype opened 5 years ago

matchatype commented 5 years ago

I'm trying to implement react-redux-firebase v3 alpha with Gatsby v2, but I think I have issues with SSR.

I am passing the new ReactReduxFirebaseProvider component and registering it inside wrapRootElement in gatsby-browser.js. Everything works well in dev, but the project doesn't build. I couldn't find any useful documentation so far.

Here is a repo with the setup: https://github.com/matchatype/react-redux-firebase-gatsby

matchatype commented 5 years ago

I was able to work around the issue by tweaking the configuration object, but... man, it looks terrible 🤦‍♂

import React from "react"
import { Provider } from "react-redux"
import { ReactReduxFirebaseProvider } from "react-redux-firebase"
import { createFirestoreInstance } from "redux-firestore"
import { ThemeProvider } from "styled-components"

import firebase from "../services/firebase"
import store from "../store"
import { theme } from "../theme"

const rrfProps = {
  firebase: typeof window !== "undefined" ? firebase : {},
  config: {
    attachAuthIsReady: true,
    firebaseStateName: "firebase",
    presence: "presence",
    sessions: "sessions",
    useFirestoreForProfile: true,
    userProfile: "users",
  },
  dispatch: store.dispatch,
  createFirestoreInstance,
}

// Setup react-redux so that connect HOC can be used
export default ({ element }) => {
  return (
    <Provider store={store}>
      <ReactReduxFirebaseProvider {...rrfProps}>
        <ThemeProvider theme={theme}>{element}</ThemeProvider>
      </ReactReduxFirebaseProvider>
    </Provider>
  )
}
prescottprue commented 5 years ago

Thanks for reporting, haven't tried the newer version with SSR much so glad to have this pointed out.

illuminist commented 5 years ago

From my experience with NextJS SSR is the server side has already initiated firebase instance. And when second client request a page, a server will try to initiate firebase instance again for a second time but fails to do so and that throws the error. Not sure how Gatsby work though.

My code to prevent the issue is:

const initFirebase = (appName = '[DEFAULT]') => {
  if (firebase.apps.find(app => app.name === appName)) {
    return
  }

  firebase.initializeApp(fbConfig, appName)
  firebase.firestore()
  firebase.functions()
  firebase.storage()

  firebase.auth().onAuthStateChanged(user => {
    if(user && process.browser){
      initMessaging(user)
    }
  })
}
matchatype commented 5 years ago

Firebase's initialization references the window object. This isn't a problem in development, but running gatsby build is a bit different. window does not exist in the node environment and you have to be careful where and when you try to access the object.

Normally I would use a singleton to inizialize firebase, then call it within a component's lifecycle method, where we can be pretty confident the window object exists. However, here we pass firebase to the context provider, so the legwork should be done by the library.

illuminist commented 5 years ago

@matchatype That's weird. My NextJS server also doesn't have window object but able to initialize successfully, and able to load data from firestore within node environment. I think the main purpose of doing SSR is to initially fetch a data to be rendered into <meta /> or a page itself. Skipping firebase totally seem to defeat it.

matchatype commented 5 years ago

@illuminist with Gatsby what you (usually) want is to structure data through gatsby's data layer. In that case, you will use gatsby node api (which creates pages at some point in the bootstrap sequence) to retrieve your data and then query from a component with graphql. Yes, you can still use unstructured data, but the tradeoffs of foregoing gatsby’s data layer are quite important, and you are foregoing the optimizations provided by transformer plugins.

To be honest @prescottprue I'm not sure how to best make use of react-redux-firebase here, so I'd like to know your point of view. Yes, authentication is a breeze with rrf, and probably a reason good enough to use it anyway. However, to get the most out of Gatsby one would be better off sourcing firebase/firestore using one of the plugins, while these bindings make sense only on the fully hydrated react app at runtime.

Sorry if this is dangerously related more to Gatsby than the library itself, hope you don't mind explaining some more.

prescottprue commented 5 years ago

All good! Yeah I might go with using a connector or writing your own connector logic to load data through node/graphql as you mentioned.

I have actually used Gatsby quite a bit - though I have mostly just connected to Firebase within the front end I could totally understand wanting to load data in the normal node/graphql way using the firebase admin SDK. I would be open to working on a connector together if you are interested 🤓

Shubhon9 commented 4 years ago

i tried this and got an error at stating Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.

prescottprue commented 4 years ago

@Shubhon9 make sure you are using the correct version as the above provided code is from beta versions (available on the next branch - can be installed using npm i react-redux-firebase@next). The reason being that ReactReduxFirebaseProvider is not defined in earlier versions, and that error can appear when trying to render