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 555 forks source link

bug(examples): SSR recipe is using old API (store.firebase is undefined) #815

Open lesmo opened 4 years ago

lesmo commented 4 years ago

What is the current behavior?

Following the SSR recipe, the store.firebase property is undefined and thus it's not possible to follow it:

import { createStore, applyMiddleware, combineReducers } from 'redux';
import { firebaseReducer } from 'react-redux-firebase'
import { firestoreReducer } from 'redux-firestore';
import reduceReducers from 'reduce-reducers';

const reducers = combineReducers({
  firebase: firebaseReducer,
  firestore: firestoreReducer,
  local: reduceReducers([... someReducers])
});

const store = createStore(persistedReducer, initialState);
store.firebase // <- undefined
store.firestore // <- undefined

I'm trying to get this to work under Razzle/Express. I understand that both firebase and firestore won't be defined at this point (after all we're just setting up reducers here), but I've found no pointers as to when or where are these ever defined or how to get react-redux-firebase to define them.

What is the expected behavior? store.firestore and store.firebase to not be undefined, or documentation to be more clear about when or how these are set...

Which versions of dependencies, and which browser and OS are affected by this issue? Did this work in previous versions or setups?

{
  "redux": "^4.0.4",
  "react-redux-firebase": "^3.0.6",
  "redux-firestore": "^0.11.0",
  "reduce-reducers": "^1.0.4",
}
harish-aka-shivi commented 4 years ago

You can try calling store.getState() method of the store. It will give the current state of firebase and firestore

lesmo commented 4 years ago

Thanks for the answer, but that’s not what I’m trying to do. I’m trying to “preload” the store’s state prior to rendering it and returning it from the server.

On Monday, Dec 30, 2019 at 3:36 AM, Harish Rana <notifications@github.com (mailto:notifications@github.com)> wrote:

You can try calling store.getState() method of the store. It will give the current state of firebase and firestore

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub (https://github.com/prescottprue/react-redux-firebase/issues/815?email_source=notifications&email_token=AAQ77ZBAJHWCMJSI5T3WR4TQ3G6LDA5CNFSM4KBGVCXKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEHZ5AMI#issuecomment-569626673), or unsubscribe (https://github.com/notifications/unsubscribe-auth/AAQ77ZAY66RR5VU4SIIBBA3Q3G6LDANCNFSM4KBGVCXA).

harish-aka-shivi commented 4 years ago

I agree, but I think createStore is a synchronous function. So the state should be initialized as soon as we call it. So you should be able to send the state to the client and use it as described the Redux SSR documentation. And regarding how firebase is initialized, It is done by initializeApp function of firebase and it is extended using createFirestoreInstance. Please let me know if I am not understanding your question.

lesmo commented 4 years ago

Yeah, I believe I haven't explained myself as good. What I want to achieve is preload data into the Redux store during Server Side Rendering.

According to the documentation, this can be achieved by calling the store.firestore.get() method after the store has been created. However, the store.firestore property is undefined after the store is created... so it cannot be done.

For now I've taken a different approach, pre-filling the Redux's firestore property myself before running React's rendering. It works, but some how doesn't feel proper.

harish-aka-shivi commented 4 years ago

Hmm, I get it. Actually someone just asked the same question on RRF Gitter community. He had used getFirebase to workaround the issue. Please check if there is getFirestore method on the store. In any case, the documentation needs some change to provide clarity on the issue.

Heyo, I'm trying to figure out the SSR stuff with react-redux-firebase. In the docs, it seems to indicate that there should be a firebase instance on the store, but I'm not seeing one. Where does that instance normally get added? I'm currently importing getFirebase from react-redux-firebase to get around it, but I feel like this may be an issue in the future if I don't figure it out. Also, when I use await getFirebase().promiseEvents([...]), it doesn't actually populate anything in the Redux store. There are no errors or anything, though. FWIW, I'm using Next.js.

lesmo commented 4 years ago

Hmm, I get it. Actually someone just asked the same question on RRF Gitter community.

I think I stumbled upon that question when I was researching this problem.

He had used getFirebase to workaround the issue. Please check if there is getFirestore method on the store. In any case, the documentation needs some change to provide clarity on the issue.

Totally! I'm pretty sure is more of a documentation issue than anything else. I'm no longer trying to get this solution working, but I can tell you that the store object didn't have getFirestore defined after creating it.

Perhaps when I get some freetime I'll try to make a repro and further research the issue.

prescottprue commented 4 years ago

React context is used now instead of placing the extended Firebase instance on the store (under store.firebase). In v3 store.firebase and store.firestore being undefined is expected. I agree it is probably an issue of docs not being up to date

komachi commented 4 years ago

React context is hard to use on server as it require to render app twice. So enhancers on store makes everything much simpler and faster.

I wanted to preload data from firestore, and I don't like the idea of populating store by hand. I found that redux-firestore still exports reduxFirestore enhancer, and it works as expected. So here is my solution.

// create store with enhancer
import { createStore, compose } from 'redux';
import { createFirestoreInstance, reduxFirestore } from 'redux-firestore';
import firebase from './firebase';
import rootReducer from './reducers';

export function configureStore() {
  return createStore(
    rootReducer,
    {},
    compose(
      reduxFirestore(firebase)
    )
  );
}
// check out react-router-config README
const routes = [
  {
    path: '/test/path',
    exact: true,
    component: TestContainer,
    // we call loadData on server, and it returns promise
    // so we can wait for it to finish before sending page to client
    loadData(firestore, branch) {
      return firestore.get('collection_name');
    }
  }
];
export default routes;
// on server side
import { StaticRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { ReactReduxFirebaseProvider } from 'react-redux-firebase';
import { matchRoutes } from 'react-router-config';
import { configureStore } from './store';
import routes from './routes';
import App from './App';

const store = configureStore();
const routerContext = {};

const handleUniversalRender = (req, res) => {
  const template = (
    <Provider store={store}>
      <ReactReduxFirebaseProvider {...rrfProps}>
        <StaticRouter
          location={req.url}
          context={routerContext}
        >
          <App />
        </StaticRouter>
      </ReactReduxFirebaseProvider>
    </Provider>
  );
  const branch = matchRoutes(routes, req.url);
  const route = branch && branch[0] && branch[0].route;
  if (route && route.loadData) {
    return route.loadData(store.firestore, branch[0])
      .then(() => template); // store populated, hurray
  }
  return Promise.resolve(template);
}
devth commented 4 years ago

Is there not a simple way to pull stuff out of react context in getInitialProps?