Reputation:
I am making a Server Side Rendering application using Next Js (React SSR).
Index.js (Simply calling another component Layout in index)
import Layout from "./layout";
import React from "react";
class Home extends React.Component {
render() {
return (
<div>
<Layout />
</div>
);
}
}
export default Home;
Layout.js
import React from "react";
import Product from "./product";
class Layout extends React.Component {
static async getInitialProps() {
const res = await fetch("https://api.github.com/repos/developit/preact");
console.log(res);
const json = await res.json();
return { stars: json.stargazers_count };
}
componentDidMount() {
if (localStorage.getItem("userLoggedIn")) {
//Get products details for logged in user api
//For now let us consider the same api
// const res = fetch("https://api.github.com/repos/developit/preact");
// const json = res.json(); // better use it inside try .. catch
// return { stars: json.stargazers_count };
} else {
// Get product details for guest user api
//For now let us consider the same api
// const res = fetch("https://api.github.com/repos/developit/preact");
// const json = res.json();
// return { stars: json.stargazers_count };
}
}
render() {
return (
<div>
This is layout page
<Product stars={this.props.stars} />
</div>
);
}
}
export default Layout;
Complete simple application example is here: https://codesandbox.io/s/nextjs-getinitialprops-748d5
My problem here is, I am trying to pass props to product
page from layout
page and props are received from getInitialProps
(SSR).. But you could see in the example provided, the props didn't works and it still gives undefined for this.props.stars
.
If I move this code into componentDidMount
and using setState
and passing state as props would work but that will not display the data fetched from api in view page source.
Note: Please don't move this logic into index.js file where it works but in my real application It is dynamic routing page and I will fetch api as per the query param fetched on that page.
If you get into this link https://748d5.sse.codesandbox.io/ and click ctrl + u
then you would see the source where I am also need the dynamic content fetched from api.
To achieve this dynamic content(stars count here in this example) display in view source only I am doing all these which is meant for SEO purpose.
Upvotes: 4
Views: 6044
Reputation: 19772
In short, you can't call getInitialProps
from a child component: getInitialProps caveats.
getInitialProps can not be used in children components, only in the default export of every page
Your Layout
page is a child component of Home
.
If you want to call getInitialProps
, then you'll either need to define getInitialProps
from within the Home
page OR create a wrapper component that calls its own getInitialProps
that will wrap the page's default export PageComponet
with this wrapper component: export default withStarsData(PageComponent)
; as a result, this wrapper component will then pass PageComponent
some props
.
If you want to make this function flexible/dynamic, then use ctx's query
parameter with a dynamic route.
This is a rather complex solution to understand, but in brief, it allows the home (/
) and layout (/layout
) pages to fetch the same data. If you don't want to fetch the data multiple times, but instead, fetch ONCE and then share between pages, then you'll want to use a higher-order state provider like redux.
Working example:
components/withStars/index.js
import React from "react";
import fetch from "isomorphic-unfetch";
// 1.) this function accepts a page Component and...
const withStars = WrappedComponent => {
// 7.) if stars data is present, returns <WrappedComponent {...props} /> with props
// else if there's an error, returns the error
// else returns a "Loading..." indicator
const FetchStarsData = props =>
props.stars ? (
<WrappedComponent {...props} />
) : props.error ? (
<div>Fetch error: {props.error} </div>
) : (
<div>Loading...</div>
);
// 3.) this first calls the above function's getInitialProps
FetchStarsData.getInitialProps = async ctx => {
// 4.) here's where we fetch "stars" data
let stars;
let error;
try {
const res = await fetch("https://api.github.com/repos/developit/preact");
const json = await res.json();
stars = json.stargazers_count;
} catch (e) {
error = e.toString();
}
// 5.) optionally this will call the <WrappedComponent/>
// getInitialProps if it has been defined
if (WrappedComponent.getInitialProps)
await WrappedComponent.getInitialProps(ctx);
// 6.) this returns stars/error data to the FetchStarsData function above
return { stars, error };
};
// 2.) ...returns the result of the "FetchStarsData" function
return FetchStarsData;
};
export default withStars;
Upvotes: 6