GithubHelp home page GithubHelp logo

Comments (10)

davnicwil avatar davnicwil commented on July 19, 2024

Hi @kandarppatel -- you're welcome and really glad you're finding react-frontload useful.

I'd love to help out here if it's a specific problem you can identify with usage of react-frontload, but I'll need a complete example including how the above calls are used in a frontload Component and probably your SSR setup too, to be able to spot any potential issues.

Off the top of my head, though, I'll give it a shot - inconsistent SSR with react-frontload is usually the result of forgetting to return a Promise somewhere in a frontload. When that happens, the async logic in the frontload may sometimes resolve in time for final render by chance, and other times won't. Double check all your frontloads for this.

from react-frontload.

kandarppatel avatar kandarppatel commented on July 19, 2024

Thank you for quick help, please use below details setup to understand in details.

server.js

import path from 'path';
import fs from 'fs';
import express from 'express';
import axios from 'axios';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router';
import { Provider } from 'react-redux';
import { Frontload, frontloadServerRender } from 'react-frontload';
import Loadable from 'react-loadable';
import Helmet from "react-helmet";

import routes from '../routes';
import reducers from '../reducers';
import createStore from '../store';

const app = new express();

app.use('/public', express.static(path.join(__dirname, '../../../public')))

app.use('/favicon.ico', (req, res) => {
  return;
});

const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require('../../../webpack.config');
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, { serverSideRender: true, noInfo: true, publicPath: config.output.publicPath }));
app.use(webpackHotMiddleware(compiler));

const server = express();
app.use((req, res) => {
    const initialState = {};
    const { store } = createStore(req.url);

    const context = {};
    const modules = [];

    const initialComponent = (
        <Loadable.Capture report={moduleName => modules.push(moduleName)}>
          <Provider store={store}>
            <StaticRouter location={req.url} context={context}>
              <Frontload isServer={true}>
                { routes }
              </Frontload>
            </StaticRouter>
          </Provider>
        </Loadable.Capture>
    );

    frontloadServerRender(() =>
          renderToString(initialComponent)
    ).then(routeMarkup => {
          if (context.url) {
            res.writeHead(302, {
            Location: context.url
          });
          res.end();
        } else {
          const helmet = Helmet.renderStatic();
          const app_store = JSON.stringify(store.getState()).replace(/</g, '\\u003c');
          res.send(renderFullPage(routeMarkup, helmet, app_store));
        }
      });
});

function renderFullPage(html, head, initialState) {
  return `
	<!DOCTYPE html>
		<html ${head.htmlAttributes.toString()}>
			<head>
        ${head.title.toString()}
        ${head.meta.toString()}
        ${head.link.toString()}
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <link rel="stylesheet" href="/public/css/style.css">
        ${head.script.toString()}
			</head>
			<body>
				<div id="app">${html}</div>
				<script>window.__PRELOADED_STATE__ = ${initialState}</script>
        <script src="/public/vendor.js"></script>
        <script src="/public/client.js"></script>
			</body>
		</html>
	`
}

app.get('*', function(req, res) {
	res.status(404).send('Server.js > 404 - Page Not Found');
})

app.use((err, req, res, next) => {
  res.status(500).send("Server error");
});

process.on('uncaughtException', evt => {
  console.log( 'uncaughtException: ', evt );
})

const port = process.env.PORT || 7013;
const env = process.env.NODE_ENV || 'production';

Loadable.preloadAll().then(() => {
  app.listen(port, err => {
    if (err) {
      return console.error(err);
    }
    console.info(`Server running on http://localhost:${port} [${env}]`);
  });
});

store.js

import { createStore, applyMiddleware, compose } from 'redux';
import { connectRouter, routerMiddleware } from 'connected-react-router';
import thunk from 'redux-thunk';
import promise from 'redux-promise';
import { createBrowserHistory, createMemoryHistory } from 'history';
import reducers from './reducers';

// A nice helper to tell us if we're on the server
export const isServer = !(
  typeof window !== 'undefined' &&
  window.document &&
  window.document.createElement
);

export default (url = '/') => {
  // Create a history depending on the environment
  const history = isServer
    ? createMemoryHistory({
        initialEntries: [url]
      })
    : createBrowserHistory();

  const enhancers = [];

  // Dev tools are helpful
  if (process.env.NODE_ENV === 'development' && !isServer) {
    const devToolsExtension = window.devToolsExtension;

    if (typeof devToolsExtension === 'function') {
      enhancers.push(devToolsExtension());
    }
  }

  const middleware = [promise, thunk, routerMiddleware(history)];
  const composedEnhancers = compose(
    applyMiddleware(...middleware),
    ...enhancers
  );

  // Do we have preloaded state available? Great, save it.
  const initialState = !isServer ? window.__PRELOADED_STATE__ : {};

  // Delete it once we have it stored in a variable
  if (!isServer) {
    delete window.__PRELOADED_STATE__;
  }

  // Create the store
  const store = createStore(
    connectRouter(history)(reducers(history)),
    initialState,
    composedEnhancers
  );

  return {
    store,
    history
  };
};

reducers.js

import { combineReducers } from 'redux';
import AppReducer from '../container/app/app_reducer';
import CatalogReducer from '../container/catalog/catalog_reducer';
import ArticleReducer from '../container/article/article_reducer';
import { reducer as formReducer } from 'redux-form';
import { reducer as modalReducer } from 'react-redux-modal';
import { connectRouter } from 'connected-react-router';

const rootReducer  = (history) =>  combineReducers({
  router: connectRouter(history),
  form: formReducer,
  modals: modalReducer,
  app: AppReducer,
  catalog: CatalogReducer,
  article: ArticleReducer
});

export default rootReducer;

index.js (Client Side - Reference)

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter, routerMiddleware } from 'connected-react-router';
import { Frontload } from 'react-frontload';

import routes from '../routes';
import createStore from '../store';

const { store, history } = createStore();

ReactDOM.render(
  <Provider store={store}>
    <ConnectedRouter history={history}>
      <Frontload noServerRender={true}>
        { routes }
      </Frontload>
    </ConnectedRouter>
  </Provider>
  , document.querySelector('#app'));

business_list.js (Container Which Calls above actions)

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { frontloadConnect } from 'react-frontload';
import { fetchCategories } from './catalog_action';
import { Link } from 'react-router-dom';
import Helmet from "react-helmet";
import { IMAGE_URL, DEFAULT_IMAGE } from '../../system/config';
import CardSmall from './card_small';

const loadCategories = async props =>
  await props.fetchCategories();

class CatalogCategory extends Component {

  constructor(props) {
      super(props);
  }

  componentDidMount() {
    this.props.fetchCategories();
  }

  renderCategories() {
    return this.props.categories.map((category) => {
        return (
          <div className="col-sm-3 col-xs-6" key={category.category_id}>
            <CardSmall article={category} type="business" />
          </div>
        );
    });
  }

  render() {

    const { categories } = this.props;

    if(!categories) {
        return <div className="home-container">Loading...</div>;
    }

  return (
      <div className="">
        <Helmet>
          <title>Business Categories & Resources</title>
          <meta name="description" content="Business Tools & Resources" />
        </Helmet>
      <div className="home-container">
          <div className="home-box">
            <h1 className="home-title">Categories</h1>
            <div className="home-sub-title">Business Categories & Resources</div>
          </div>
        </div>
        <div className="container">
          <div className="row popular-articles">
            {this.renderCategories()}
          </div>
        </div>
      </div>
    );
  }
}

function mapStateToProps(state) {
  return {
    categories: state.catalog.categories
  };
}

export default connect(mapStateToProps, { fetchCategories })(
  frontloadConnect(loadCategories, {
    onMount: true,
    onUpdate: false
  })(CatalogCategory)
);

webpack.config.js

module.exports = {
  mode: 'production',
  entry: {
    client: './src/system/client/index.js',
    vendor: ['react', 'react-dom', 'react-router-dom'],
  },
  output: {
    path: __dirname,
    publicPath: '/',
    filename: 'public/[name].js'
  },
  module: {
    rules: [
      { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
    ]
  },
  resolve: {
    extensions: ['*', '.js', '.jsx']
  },
  devServer: {
    port: 7013,
    historyApiFallback: true,
    contentBase: './'
  },
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          chunks: 'all'
          }
        }
    },
  }
};

.babelrc

{
  presets:
    [
        "@babel/preset-env",
        "@babel/preset-react"
    ],
    plugins:
    [
        "@babel/plugin-proposal-object-rest-spread",
        "@babel/plugin-transform-runtime",
        "@babel/plugin-transform-async-to-generator",
        "@babel/plugin-proposal-class-properties"
    ]
}

I hope all above will help you to understand our setup and specific issue. Let me know if you need anything else.

Once again thank you for your help.

from react-frontload.

davnicwil avatar davnicwil commented on July 19, 2024

Great - so what's a specific scenario of the bug you're seeing?

Which route is rendered, and then what is rendered in the success case and failure case?

from react-frontload.

kandarppatel avatar kandarppatel commented on July 19, 2024

Issue is for Server Side Rendering, if we use the action format 1 mentioned than it all works completely fine but when we use action format 2 with dispatch method than its not working consistently, sometimes waiting for promise and getting the perfect data and sometimes missing the data so not sure why its not working consistently and getting data all the time.

We have tested this in Browser View Page Source using refresh, First time we load its coming, than reload than yes coming than 3rd time not, than 4th time not, than 5th time yes so its randomly waiting for promise results or its not at all checking all the promises from above dispatch based actions format, if data getting faster from api than its passing the data instantly and if api takes little longer than its not passing at all .

Using action format 1 mentioned below (using direct return without dispatch), it all works perfect and consistent.

Action Format 1 (Working Fine) :

export function fetchCategories() {     
    const request = axios.get('${API_URL}/blog/categories');  
   return{  
        type: FETCH_CATEGORIES,  
        payload: request 
  };
}

Action Format 2 (Working Randomly, Not Consistent) :

export function fetchCategories() {
  return function(dispatch) {
    return axios.get('${API_URL}/blog/categories')
    .then(response => {
      dispatch({
        type: FETCH_CATEGORIES,
        payload: response
      });
    })
    .catch((error) => {
      console.log(error);
    })
  }
}

I hope now you will clearly understand the issue.

from react-frontload.

davnicwil avatar davnicwil commented on July 19, 2024

I think this is because you're not returning the result of the dispatch in the second method, which is a promise with redux-thunk.

This would mean that the action creator isn't returning the promise of the request, meaning the frontload also isn't returning the promise, meaning react-frontload doesn't wait for the request to finish on server renders.

This would explain the random behaviour you're seeing with it sometimes loading in time by chance, if the api is quick, and sometimes not if it's a bit slower.

Give this a try, and let me know if it fixes it, and if that all makes sense:

.then(response => (
  dispatch({
    type: FETCH_CATEGORIES,
    payload: response
  })
))

(note I just turned the {} braces into () for immediate return)

from react-frontload.

kandarppatel avatar kandarppatel commented on July 19, 2024

Sorry for replying little late but have tried it and its giving syntax error.

Will do more research and update you once find any solution.

from react-frontload.

davnicwil avatar davnicwil commented on July 19, 2024

Hm, you shouldn't be getting any syntax error from just returning at that point. What is the error?

Did the explanation about the Promises make sense? I don't think you need to do any more research, I think that's the problem :-)

from react-frontload.

davnicwil avatar davnicwil commented on July 19, 2024

Hey @kandarppatel, any joy with what I suggested?

from react-frontload.

kandarppatel avatar kandarppatel commented on July 19, 2024

Yes finally it worked out, not sure what was the exact issue but may be in thunk promise middle-ware but its all working fine now. Thank you so much for your support.

from react-frontload.

matthewlein avatar matthewlein commented on July 19, 2024

I had the same problem, it turns out I was not returning my dispatched actions. @davnicwil Thanks for the tip.

For an example, this is now a working thunk

export function fetchUser(): RequestThunk {
  return (dispatch) => {
    dispatch(UserActions.USER_FETCH_PENDING());

    return axios
      .get('users/me')
      .then((response) => {
        return dispatch(UserActions.USER_FETCH_SUCCESS(response.data));
      })
      .catch((error) => {
        return dispatch(UserActions.USER_FETCH_ERROR(error));
      });
  };
}

How it it worked in v1...I'll never know

from react-frontload.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.