How to render landing page and logged in first page on path="/" without page flickering when using SSR?

Hi there,

Note: To quickly understand my issue you can read through the text and skip the code, the second time around you then read the code which interest you or all of it.

I am using SSR and I am rendering landing page on path="/" and this works fine before I try to render the page the user must see upon login. So on react router I have something like (only path="/" and {PublicMenu} are important):

export const Routes = () =>
    <App>
        <Route exact path="/" component={PublicMenu}/>
        <Route path="/howItWorks" component={HowItWorks}/>
        <Route path="/registerInfo" component={RegisterInfo}/>
        <Route path="/login/:nextPage/:previousPage" component={Login}/>
        .
        .
        .
    </App>

On the {PublicMenu} component upon login I try to add the logic to render the page the user must see upon login; the result of this is the landing page is first rendered and then the login page is rendered hence the flickering. The landing page is rendered by SSR way before chrome debugging even gets a chance to step through the code.

Here is the App code as per react router above in case you are interested in the App code:

import React, { Component } from 'react' //PropTypes
import { Link } from 'react-router-dom'
import { withTracker } from 'meteor/react-meteor-data';
import Loadmang from './utilities/Loadmang';

class App extends Component {

  render() {
  
	if (self.props.page == "logout")
	{	
		userId = Meteor.userId();
		if (Meteor.isClient) {
			Meteor.logout(function(err) { 
				if (!err)
				{
					localStorage.removeItem("isLoggedIn")
					path = window.location.origin + "/"
					window.location.replace(path);
					//this.props.history.push("/");
					//return;
				}
			});
			return;
		}
	}
	
	var children = this.props.children
	if (this.props.user)
		return (<div>
			<nav className="navbar navbar-fixed-top navbar-color-on-scroll">
				<div className="container">
					<div className="collapse navbar-collapse" id="navigation-index">

						<ul className="nav navbar-nav navbar-left mobile-nav">
							<li class="nav-link"><Link to="/store">Menu</Link></li>

							<li className="nav-link"><Link to="/howItWorks">How it works</Link></li>
							<li className="nav-link"><Link to="/about">Contact us</Link></li>
						</ul>

						<ul className="nav navbar-nav navbar-left">
							<li className="nav-link nav-li-dsn-L"><Link to="/store/knowYouBetterQuestionOptions/checkDetails">Profile</Link></li>
							<li className="nav-link nav-li-dsn-L"><Link to="/logout">Logout</Link></li>
						</ul>
						<ul className="nav navbar-nav navbar-right">
							<li className="nav-li-dsn-M"><Link to="/store/knowYouBetterQuestionOptions/checkDetails">Profile</Link></li>
							<li className="nav-li-dsn-M"><Link to="/logout">Logout</Link></li>
						</ul>
						<div className="clearfix">
						</div>
					</div>
				</div>
			</nav>

			<div className="wrapper">
				<div className="container">
					<div className="main container-fluid">
						<div className="space-65"></div>
						<div className="row">
							<div className="col-xs-10 col-xs-offset-1 col-lg-8 col-lg-offset-2">
								{ children }
								

								
						
							
							</div>
						</div>
						<div className="col-xs-10 col-xs-offset-1 col-lg-8 col-lg-offset-2">
						</div>
					</div>
				</div>
			</div>
		</div>)
	else
		return (<div>
			<nav className="navbar navbar-fixed-top navbar-color-on-scroll">
				<div className="container">
					<div className="collapse navbar-collapse" id="navigation-index">

						<ul className="nav navbar-nav navbar-left mobile-nav">
							<li className="nav-link"><Link to="/home">Home</Link></li>

							<li className="nav-link"><Link to="/howItWorks">How it works</Link></li>
							
							<li className="nav-link"><Link to="/about">Contact us</Link></li>
						</ul>

						<ul className="nav navbar-nav navbar-left">
							<li className="nav-link nav-li-dsn-L"><Link to="/registerInfo">Register</Link></li>
							<li className="nav-link nav-li-dsn-L"><Link to="/login/anonymousGenBody/anonymousGenBody">Login</Link></li>
						</ul>
						<ul className="nav navbar-nav navbar-right">
							<li className="nav-li-dsn-M"><Link to="/registerInfo">Register</Link></li>
							<li className="nav-li-dsn-M"><Link to="/login/anonymousGenBody/anonymousGenBody">Login</Link></li>
						</ul>
						<div className="clearfix">
						</div>
					</div>

				</div>

			</nav>

			<div className="wrapper">
				<div className="container">
					<div className="main container-fluid">
						<div className="space-65"></div>
						<div className="row">
							<div className="col-xs-10 col-xs-offset-1 col-lg-8 col-lg-offset-2">
								{ children }
								

								
						
							
							</div>
						</div>
						<div className="col-xs-10 col-xs-offset-1 col-lg-8 col-lg-offset-2">
						</div>
					</div>
				</div>
			</div>
		</div>)
	}
}

export default withTracker( function(props) {

	var user;
	var group;
	var groupMember;
	var providers;
	var customerAccount;
	if (Meteor.isClient) {
		const subscription = Meteor.subscribe('secondMemberId');
			
		return {
			loading1: !subscription.ready(),
			user: Meteor.users.findOne({_id:Meteor.userId()})
		}
	}
	else
	{
		return {
			loading1: true,
			user: user
		}
	}
})(App);

Here is the PublicMenu component code below. Please note that localStorage’s “isLoggedIn” in the code is set to “true” upon logging in - I have not pasted a snippet of that code. Note that if isLoggedIn was true when checked on the server there would be no flickering as both the client
and the server would have the exact same html rendered. Here is the PublicMenu code:

import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import { withTracker } from 'meteor/react-meteor-data';

//const PublicMenu = function() {
export class PublicMenu extends Component {

	constructor(props){
		super(props);

		this.state = {
		  isLoggedIn : false
		};
	}
	
	componentWillMount () {
		var isLoggedIn;
		if (Meteor.isClient)
			isLoggedIn = localStorage.getItem("isLoggedIn");
		if (isLoggedIn)
			this.setState({isLoggedIn: true});
	}
	
	render() {
		const self = this;
		var isLoggedIn
		if (this.state)
			isLoggedIn = this.state.isLoggedIn;
		if (isLoggedIn)
		{
			return (<div>
				{Meteor.isClient
					? <div>
						<header>
							<h1 className="header1">Menu</h1>
						</header>
						<div  className="outerList">
							<Link className="innerList anchor" to="/store/providerList">Data/Airtime/Electricity</Link>
							<Link className="innerList anchor" to="/store/shareSchemeQuetion">Cash back program</Link>
							<Link className="innerList anchor" to="/store/userBalance">Check balance</Link>
							<Link className="innerList anchor" to="/store/knowYouBetterQuestionOptions/checkDetails">Profile</Link>
							<Link className="innerList anchor" to="/store/searchPurchaseHistory">Purchase/Invoice history</Link>
							<Link className="innerList anchor" to="/store/checkMemberId">Check member ID</Link>
							<Link className="innerList anchor" to="/store/depositMoney">Deposit money</Link>
						</div>
					</div>
					: <div>
						<header>
							<h1 className="header1">Menu</h1>
						</header>
						<div  className="outerList">
							<div className="innerList anchor" >Data/Airtime/Electricity</div>
							<div className="innerList anchor" >Cash back program</div>
							<div className="innerList anchor" >Check balance</div>
							<div className="innerList anchor" >Profile</div>
							<div className="innerList anchor" >Purchase/Invoice history</div>
							<div className="innerList anchor" >Check member ID</div>
							<div className="innerList anchor" >Deposit money</div>
						</div>
					</div>
				}
			</div>)
		}
		else
		{
			return (<div>
				<header>
					<h1 className="header1">Menu</h1>
				</header>
				{Meteor.isClient
					? <div  className="outerList">
						<Link className="innerList anchor" to="/registerInfo">How?</Link>
						<Link className="innerList anchor" to="/store/providerList">Data/Airtime/Electricity</Link>
						<Link className="innerList anchor" to="/store/opened_discountsAccumulated">Funds Raised</Link>
						<Link className="innerList anchor" to="/store/opened_userBalance">Check balance</Link>
						<Link className="innerList anchor" to="/store/opened_searchPurchaseHistory">Purchase/Invoice history</Link>
						<Link className="innerList anchor" to="/store/opened_checkDetails">Profile</Link>
						<Link className="innerList anchor" to="/store/opened_checkStokvelId">Check group ID</Link>
						<Link className="innerList anchor" to="/store/opened_stokvelMembers">Group members</Link>
						<Link className="innerList anchor" to="/store/opened_depositMoney">Deposit money</Link>
						<Link className="innerList anchor" to="/store/opened_checkBankDetails">Group bank details</Link>
						<Link className="innerList anchor" to="/store/opened_membersToApproveBankDets">Members still to approve bank details</Link>
						<Link className="innerList anchor" to="/store/opened_approveBankDetails">Approve Bank details</Link>
						<Link className="innerList anchor" to="/store/opened_checkMemberId">Check member ID</Link>
						<Link className="innerList anchor" to="/store/opened_deregisterFromGroup">Deregister from group</Link>
						<Link className="innerList anchor" to="/store/opened_swapAdminRole">Swap admin role with another member</Link>
					  </div>
					: <div  className="outerList">
						<div className="innerList anchor" >How?</div>
						<div className="innerList anchor" >Data/Airtime/Electricity</div>
						<div className="innerList anchor" >Funds Raised</div>
						<div className="innerList anchor" >Check balance</div>
						<div className="innerList anchor" >Purchase/Invoice history</div>
						<div className="innerList anchor" >Profile</div>
						<div className="innerList anchor" >Check group ID</div>
						<div className="innerList anchor" >Group members</div>
						<div className="innerList anchor" >Deposit money</div>
						<div className="innerList anchor" >Group bank details</div>
						<div className="innerList anchor" >Members still to approve bank details</div>
						<div className="innerList anchor" >Approve Bank details</div>
						<div className="innerList anchor" >Check member ID</div>
						<div className="innerList anchor" >Deregister from group</div>
						<div className="innerList anchor" >Swap admin role with another member</div>
					  </div>
				}
			</div>)
		}
	}
}

export default withTracker( function(props) {

	var user;
	var group;
	if (Meteor.isClient) {
		const subscription = Meteor.subscribe('secondMemberId');
			
		return {
			loading1: !subscription.ready(),
			user: Meteor.users.findOne({_id:Meteor.userId()})
		}
	}
	else
	{
		return {
			loading1: true,
			user: user
		}
	}
})(PublicMenu);

How can I render landing page and logged-in first page on path="/" without page flickering when using SSR? Please note that I rather have the server render the correct page followed by the same page by the client than suppress the server page and only have the client render the page. I saw that most people out there rather have the landing page be the same as the login first page to overcome this issue. Unfortunately that is not what my client wants, in fact I have different root path pages depending on the user’s role. Thanks for helping.

Am I asking for a solution for something that does not have a solution in the meteor react context? Any reply is appreciated.

I see I cannot even use a method call that gets userId on the server, as a method call from the server does not have a user context and thus Meteor.userId() will return null; if this were possible the userId will imply isLoggedIn = true and thus render the correct html using SSR. The code snippet is as follows:

			Meteor.call("getUserId",Meteor.bindEnvironment(function(error,result) {
				isLoggedIn = result;

				if (isLoggedIn)
					//render correct html; obviously using future promise so that I can return future.wait(); on the render() method.

peerlibrary/meteor-user-extra package does not seem to work anymore (at least with newer versions of meteor) for getting userId on server without method call emanating from client but from server.

The thing is that meteor by default uses localstorage to save the logged in user token on the client and is not sent to the server. For that you will need to use cookies to store the data and send it to the server on each request so that you are able to know which user is logged in…

Unfortunately I haven’t done that and cant help much apart from this.

Hope this helps

Take a look at this thread.

This a good candidate for a SSR support package.

@pmogollon, the issue with cookies is my logic to render the required page server side will not work if the user’s browser has local cookies support turned off, and for that user I’ll be back to flickering page. Thanks for the suggestion.

And how else would you keep a memory of the user status? that’s what the cookie is for.

Thanks @alawi, I thought there might be a better way, a meteor way. In that case I’ll also use cookies.

I’ve shared a simple helper package that you can use for reference, check it out here.

I hope it helps!

3 Likes

@alawi, thank you so much for your help.