question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Component Rendering before Data fetch request in saga is completed

See original GitHub issue

Hi, I am using next.js in my project and have integrated redux, redux-saga & reselect with it. I am dispatching actions in async getIntialProps but the component renders before the saga request attached to respective action is completed.

  • I have searched the issues of this repository and believe that this is not a duplicate.

Steps to Reproduce (for bugs)

  1. I made an action and attached saga listener with it
  2. I dispatched that action in async getInitialProps of pages/index.js
  3. It should have waited before going to render of pages/index.js
  4. However, the page rendered before the saga request got completed.
  5. In this way, page renders before the store is updated with newly fetched data.

Context

Your Environment

Tech Version
next ^4.1.4
node >=6
OS linux, windows
browser all
etc

The code for certain issue is as follows:

pages/index

class Home extends React.Component {
  constructor(props) {
    super(props);
  }
  static async getInitialProps({store}) {
    await store.dispatch(getSettings())
    await store.dispatch(getAboutUs())
    console.log('Team Before',store.getState().home.Team)
    await store.dispatch(getTeam())
    console.log('Team After',store.getState().home.Team)
    await store.dispatch(getServices())
    await store.dispatch(getTestimonials())
    await store.dispatch(getPortfolio())
    await store.dispatch(getPortfolioSettings())
  }
	render() {
		return (
      <HomeContainer />
		);
	}
}
export default withReduxSaga(Home)

Home Container


import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import Home from 'components/views/Home';
import { Router } from 'routes';
import Root from 'containers/Root'
import LinearLoader from 'components/widgets/Loaders/LinearProgressLoader'

import {
	getSettings,
	getAboutUs,
	getTeam,
	getServices,
	getPortfolio,
	getPortfolioSettings,
	getTestimonials
}  from './actions';

import {
	selectSettings,
	selectGetSettingsStatus,
	selectAboutUs,
	selectGetAboutUsStatus,
	selectTeam,
	selectGetTeamStatus,
	selectServices,
	selectGetServiceStatus,
	selectTestimonials,
	selectGetTestimonialsStatus,
	selectPortfolios,
	selectGetPortfoliosStatus,
	selectPortfolioSettings,
	selectGetPortfolioSettingsStatus
} from './selectors';

export class HomeContainer extends React.Component {

	changeRoute = (route) => {
		Router.replaceRoute(route);
	}

	render() {
		console.log("Settings", this.props.Settings)
		const { GetPortfolioSettingsStatus, GetSettingsStatus, GetAboutUsStatus, GetTeamStatus, GetServicesStatus, GetTestimonialStatus, GetPortfoliosStatus } = this.props;
		if(GetSettingsStatus.get('loading')) {
			return (
				<LinearLoader />
			);
		}
		return (
  <div>
    {!!this.props.Settings && <Root Settings={this.props.Settings} isHome SettingsStatus={GetSettingsStatus} >
      <Home
        Settings={this.props.Settings}
        SettingsStatus={GetSettingsStatus}
        changeRoute={this.changeRoute}
        Team={this.props.Team}
        TeamStatus={GetTeamStatus}
        AboutUs={this.props.AboutUs}
        AboutUsStatus={GetAboutUsStatus}
        Services={this.props.Services}
        ServicesStatus={GetServicesStatus}
        Testimonials={this.props.Testimonials}
        TestimonialStatus={GetTestimonialStatus}
        Portfolios={this.props.Portfolios}
        PortfoliosStatus={GetPortfoliosStatus}
        PortfolioSettings={this.props.PortfolioSettings}
        PortfolioSettingsStatus={GetPortfolioSettingsStatus}
      />
    </Root>}
  </div>
		);
	}
}

const mapDispatchToProps = (dispatch) => {
  return {
		onGetSettings: bindActionCreators(getSettings, dispatch),
		onGetAboutUs: bindActionCreators(getAboutUs, dispatch),
		onGetTeam: bindActionCreators(getTeam, dispatch),
		onGetServices: bindActionCreators(getServices, dispatch),
		onGetPortfolio: bindActionCreators(getPortfolio, dispatch),
		onGetTestimonial: bindActionCreators(getTestimonials, dispatch),
		onGetPortFolioSettings: bindActionCreators(getPortfolioSettings, dispatch)
  }
}

const mapStateToProps = createStructuredSelector({
	Settings: selectSettings(),
	Team: selectTeam(),
	AboutUs: selectAboutUs(),
	Services: selectServices(),
	Testimonials: selectTestimonials(),
	Portfolios: selectPortfolios(),
	PortfolioSettings: selectPortfolioSettings(),
	GetSettingsStatus: selectGetSettingsStatus(),
	GetTeamStatus: selectGetTeamStatus(),
	GetAboutUsStatus: selectGetAboutUsStatus(),
	GetServicesStatus: selectGetServiceStatus(),
	GetTestimonialStatus: selectGetTestimonialsStatus(),
	GetPortfoliosStatus: selectGetPortfoliosStatus(),
	GetPortfolioSettingsStatus: selectGetPortfolioSettingsStatus()
});

export default connect(mapStateToProps, mapDispatchToProps)(HomeContainer)

Sagas


import es6promise from 'es6-promise'
import 'isomorphic-fetch'
import request from 'common/request'
import config from 'config/config'
import {
  getSettingsSuccess,
  getSettingsFail,
  getAboutUsSuccess,
  getAboutUsFail,
  getServicesSuccess,
  getServicesFail,
  getPortfolioSuccess,
  getPortfolioSettingsSuccess,
  getPortfolioSettingsFail,
  getPortfolioFail,
  getTeamSuccess,
  getTeamFail,
  getTestimonialsSuccess,
  getTestimonialsFail
} from './actions'

import {
  GET_SETTINGS,
  GET_ABOUT_US,
  GET_SERVICES,
  GET_PORTFOLIO,
  GET_TEAM,
  GET_TESTIMONIALS,
  GET_PORTFOLIO_SETTINGS
} from './constants';


es6promise.polyfill()

function * getSettings () {
  const settings = yield call(request, config.settings_type);
  console.log('Settings Fetched: ', settings)
  if(!settings) {
    yield put(getSettingsFail('No Data Found'));
  }
  else if(!settings.err) {
    yield put(getSettingsSuccess(settings[0]));
  } else {
    yield put(getSettingsFail(settings.err.reason));
  }
}

function * getAboutUs () {
  const aboutUs = yield call(request, config.about_us_type);
  if(!aboutUs) {
    yield put(getAboutUsFail('No Data Found'));
  }
  else if(!aboutUs.err) {
    yield put(getAboutUsSuccess(aboutUs[0]));
  } else {
    yield put(getAboutUsFail(aboutUs.err.reason));
  }
}

function * getServices () {
  const services = yield call(request, config.services_type);
  if(!services) {
    yield put(getServicesFail('No Data Found'));
  }
  else if(!services.err) {
    yield put(getServicesSuccess(services[0]));
  } else {
    yield put(getServicesFail(services.err.reason));
  }
}

function * getPortfolio () {
  const portfolio = yield call(request, config.portfolios_type);
  if(!portfolio) {
    yield put(getPortfolioFail('No Data Found'));
  }
  else if(!portfolio.err) {
    yield put(getPortfolioSuccess(portfolio));
  } else {
    yield put(getPortfolioFail(portfolio.err.reason));
  }
}

function * getPortfolioSettings () {
  const portfolio_settings = yield call(request, config.portfolios_settings_type);
  if(!portfolio_settings) {
    yield put(getPortfolioSettingsFail('No Data Found'));
  }
  else if(!portfolio_settings.err) {
    yield put(getPortfolioSettingsSuccess(portfolio_settings[0]));
  } else {
    yield put(getPortfolioSettingsFail(portfolio_settings.err.reason));
  }
}

function * getTeam () {
  const team = yield call(request, config.team_type);
  // console.log('teams Fetched: ', team)
  if(!team) {
    yield put(getTeamFail('No Data Found'));
  }
  else if(!team.err) {
    yield put(getTeamSuccess(team[0]));
  } else {
    yield put(getTeamFail(team.err.reason));
  }
}

function * getTestimonials () {
  const testimonials = yield call(request, config.testimonials_type);
  if(!testimonials) {
    yield put(getTestimonialsFail('No Data Found'));
  }
  else if(!testimonials.err) {
    yield put(getTestimonialsSuccess(testimonials[0]));
  } else {
    yield put(getTestimonialsFail(testimonials.err.reason));
  }
}


export function* homeSagas() {
  yield fork(takeLatest, GET_SETTINGS, getSettings);
  yield fork(takeLatest, GET_ABOUT_US, getAboutUs);
  yield fork(takeLatest, GET_SERVICES, getServices);
  yield fork(takeLatest, GET_PORTFOLIO, getPortfolio);
  yield fork(takeLatest, GET_PORTFOLIO_SETTINGS, getPortfolioSettings);
  yield fork(takeLatest, GET_TEAM, getTeam);
  yield fork(takeLatest, GET_TESTIMONIALS, getTestimonials);
}

export default homeSagas;

Home Component

import ScrollableAnchor from 'react-scrollable-anchor'
import { Router } from 'routes';
import AboutUs from './AboutUs'
import Services from './Services'
import Team from './Team'
import Testimonials from './Testimonials'
import Portfolios from './Portfolio'

class Home extends React.Component {
	constructor (props) {
		super(props)
	}

	render() {
		console.log('Teams available: ', this.props.Team)
		return (
  		<div>
		    <div className="page-header" filter-color="orange">
		      {!!this.props.Settings && <div className="page-header-image" data-parallax="true" style={{backgroundColor: "grey", backgroundImage: `url(${this.props.Settings.getIn(['metadata','home_banner_image','url'])}`}} />}
		      <div className="container">
						{
							this.props.SettingsStatus.get('loaded') ?
		        <div className="content-center brand">
		          <img src={!!this.props.Settings ? this.props.Settings.getIn(['metadata','company_logo','url']): ''} alt={this.props.Settings.getIn(['metadata','agency_name'])} />
		          <h3>{!!this.props.Settings ? this.props.Settings.getIn(['metadata','punch_line']): 'Your Punch Line Here'}</h3>
		        </div>
						:
						<div className="content-center brand">
							<h1> Loading ... </h1>
						</div>
						}
		      </div>
		    </div>
		    <div className="main">
					{
						this.props.AboutUsStatus.get('loading') ?
						<div></div>
						: this.props.AboutUsStatus.get('loaded') ?
		    	  <ScrollableAnchor id={'aboutUs'}>
							<AboutUs aboutUs={this.props.AboutUs} changeRoute={this.props.changeRoute} />
						</ScrollableAnchor>
						: <div></div>
					}

					{
						this.props.ServicesStatus.get('loading') ?
						<div></div>
						: this.props.ServicesStatus.get('loaded') ?
		      	<ScrollableAnchor id={'services'}>
					  	<Services services={this.props.Services} />
						</ScrollableAnchor>
						: <div></div>
					}

					{
						this.props.TeamStatus.get('loading') ?
						<div></div>
						: this.props.TeamStatus.get('loaded') ?
		      	<ScrollableAnchor id={'team'}>
		        	<Team team={this.props.Team} />
						</ScrollableAnchor>
						: <div></div>
					}

					{
						this.props.TestimonialStatus.get('loading') ?
						<div></div>
						: this.props.TestimonialStatus.get('loaded') ?
		      	<ScrollableAnchor id={'testimonials'}>
							<Testimonials testimonials={this.props.Testimonials} />
						</ScrollableAnchor>
						: <div></div>
					}

					{
						this.props.PortfoliosStatus.get('loading') ?
						<div></div>
						: this.props.PortfoliosStatus.get('loaded') && this.props.PortfolioSettingsStatus.get('loaded') ?
		      	<ScrollableAnchor id={'portfolio'}>
							<Portfolios portfolios={this.props.Portfolios} settings={this.props.PortfolioSettings} changeRoute={this.props.changeRoute} />
		      	</ScrollableAnchor>
						: <div> </div>
					}
		    </div>

		  </div>
		);
	}
}
export default (Home)

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
costasapostolopouloscommented, Dec 14, 2017

@musaghauri how did you solve this issue?

0reactions
zalabinccommented, Dec 19, 2017

You need to make sure saga is running, and monitor + waiting for saga.task is completed in ‘server’

https://github.com/zalabinc/nextjs-boilerpate/blob/master/app/utils/monitorSagas.js

Read more comments on GitHub >

github_iconTop Results From Across the Web

Component Rendering before Data fetch request in saga is ...
Hi All, I am trying to create a Landing page which fetches some data from API and then renders. The issue here is...
Read more >
saga fetch data after component rendered - Stack Overflow
When you place function invocations directly in the function body they will be invoked any time react decides to "render" the component to...
Read more >
How to fetch data in React with performance in mind
Shows a loading state until sidebar data is loaded first, renders sidebar, and keeps loading state until the data is finished in the...
Read more >
Isomorphic Implementation of React | by Yudhajit Adhikary
Let's discuss in details how React Router Server Rendering happens ,First Server receives request and notes path,it creates history components and injects path, ......
Read more >
React fetching data before rendering in 2020
The render gets compile error when data is not found · A child component render relies on data response from parent component ·...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found