I have a doubt in my nextjs project.
I added a new route inside my header calls /files and I don't know why takes a long time to load the data when I want to return to the home.
I console.log the request I and notice calls to my API and my INDEX twice, but I'm not sure if it's a problem.
The endpoint with the data is a little slow, but I believe that if I call it inside my pages/index getInitialProps, the data it's loaded in server at the beginning, I am right? and if I am right why is it taking so long to show me the data again?
Here is my code!
Header
import React, { Component } from "react";
class Header extends Component {
render() {
return (
<>
<Navbar collapseOnSelect expand="lg" bg="light" variant="light">
<Navbar.Toggle
aria-controls="responsive-navbar-nav"
style={{ outline: "0", display: 'none' }}
/>
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto"></Nav>
<Nav>
<Link href="/" passHref>
<Nav.Link>
Home
</Nav.Link>
</Link>
<Link href="/files" passHref>
<Nav.Link>
Files
</Nav.Link>
</Link>
</Nav>
</Navbar.Collapse>
</Navbar>
</>
);
}
}
export default Header;
pages/index
import React, { useState, useEffect } from "react";
/* Others */
import Error from "./_error";
import { getDataLatestEvents } from "../helper/api";
/* Components */
import MyComponent from "../components/MyComponent";
/* Bootstrap Components */
import Row from "react-bootstrap/Row";
const Index = props => {
console.log('index*************')
const [contentData, setData] = useState([]);
const res = props.data.data.data;
useEffect(() => {
setData(res);
}, [props]);
if (props.statusCode !== 200) {
return <Error statusCode={props.statusCode} />;
}
return (
<>
<Row>
<StyledContainer>
<MyComponent
data={contentData}
/>
</StyledContainer>
</Row>
</>
);
};
Index.getInitialProps = async ({ res }) => {
try {
let req = await getDataLatestEvents();
return { data: req, statusCode: 200 };
} catch (e) {
res.statusCode = 503;
console.log(`error /pages/index: ${e}`);
return { data: null, statusCode: 503 };
}
};
export default Index;
helper/api
import fetch from "isomorphic-unfetch";
const BASE_URL = "https://myendpoint/api";
export async function getDataLatestEvents() {
const res = await fetch(`${BASE_URL}/eventos?latest`);
let data = await res.json();
console.log('API*************')
return { data: data};
}
This sort of delay is often encountered when using next dev (via yarn dev or npm dev). This is because when using yarn dev, page is rebuild every time it is requested. So when you navigate back to the index page, Next.js first rebuild that page for you and then it is served. That's why there is a delay.
You should not find similar delay in production (when using next build and then next start)
Edit
getInitialProps enables server-side rendering in a page. In case you don't need to fetch any data every time the request is sent or page is reloaded, use getStaticProps instead.
Related
In my Next js app I'm Passing an Object through pages. what I did is compress my array of objects into JSON JSON.stringify(result) from index page and in my second page I parsed it JSON.parse(props.router.query.result). this worked great. but the issue is when reloading the page the browser prompts
This page isn’t workingIf the problem continues, contact the site owner.
HTTP ERROR 431
I know this message indicates for long url head. so is there a way for me to shorten this?
index page
<Link href=
{{pathname: "/tv-shows",
query: {
result: JSON.stringify(result),
//result: [result],
img: img,
index: index,
}}} key={index}
>
<div className=' relative snap-center h-56 w-96 rounded-3xl hover:rounded-3xl hover:scale-110 hover:transition-all hover:duration-200 hover:ease-in ease-out duration-200 '>
<Image
src={img}
layout="fill"
objectFit="cover"
className={`h-full w-full bg-cover bg-no-repeat rounded-3xl hover:rounded-3xl hover:scale-110 hover:transition-all hover:duration-200 hover:ease-in ease-out duration-200`} />
</div></Link>
in second page
const TvShows = (props) => {
const [result, setResult] = useState([]);
const [index, setIndex] = useState("");
const [img, setImg] = useState("");
useEffect(()=>{
console.log("first")
console.log(props.router.query);
if (props.router.query.result){
const query = props.router.query;
const res = JSON.parse(query.result);
setResult(res);
setIndex(query.index);
setImg(query.img);
//console.log(JSON.parse(props.router.query.result));
}
},[props.router.query ])
return (
<div className=''>
<Head>
<title>{Popular[Number(index)].title} | </title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<Background result={result} img={img} index={index} />
{/* <Background img={img} index={index} /> */}
<div className='cursor-default px-10'>
<div className=' text-xl pt-5 pb-5'>Full Episodes </div>
{/* <Contents result={result} index={index}/> */}
</div>
</div>
)
}
export default withRouter(TvShows)
please help me with the fix
Based on comments to your original post, I deducted that you do not want to shorten a very long URL, but you are trying to pass data between subpages of Next app and save it so it is accessible after page refresh. What you can do to solve your issue is saving your result to localStorage every time it changes:
useEffect(() => {
localStorage.setItem("result", JSON.stringify(result))
}, [result])
And then, in your second page read the data:
useEffect(()=>{
const result = JSON.parse(localStorage.getItem("result"))
console.log("first")
console.log(result);
if (result){
setResult(result);
setIndex(query.index);
setImg(query.img);
}
}, [])
After comments to this Answer:
I think that what you want to do is creating a page tv-shows, which will display the details of one Youtube playlist. Best way to get this working is by creating dynamic routes.
Create the following directory structure in your app:
root
└── pages
└── tv-shows
└── [index].js
Paste this into the file [index].js
import { useRouter } from "next/router";
export async function getStaticPaths() {
return {
paths: [{ params: { index: "0" } }, { params: { index: "1" } }],
fallback: false
};
}
export async function getStaticProps(context) {
const MY_PLAYLIST_1 = "PL9562CjMJkXIgaV_UA5hf1VADfn4Sqd0P";
const MY_PLAYLIST_2 = "PL9562CjMJkXIgaV_UA5hf1VADfn4Sqd0P";
const API_KEY = "AIzaSyCELe0KoZYBjonJskBMbzdlTuCow3sr3zo";
const PLAYLIST_REQUEST_URL_1 = `https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=500&playlistId=${MY_PLAYLIST_1}&key=${API_KEY}`;
const PLAYLIST_REQUEST_URL_2 = `https://youtube.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=500&playlistId=${MY_PLAYLIST_2}&key=${API_KEY}`;
const playlistResponse1 = await fetch(PLAYLIST_REQUEST_URL_1);
const playlistResponse2 = await fetch(PLAYLIST_REQUEST_URL_2);
const playlistResult1 = await playlistResponse1.json();
const playlistResult2 = await playlistResponse2.json();
return {
props: {
results: [playlistResult1, playlistResult2],
},
revalidate: 3600,
};
}
export default function TvShows({results}) {
const router = useRouter();
const { index } = router.query;
return <div>{index}, {JSON.stringify(results[index])}</div>;
}
Next step, override content of card.js with the following (just remove result variable and the query parameter)
import Link from "next/link";
const Card = ({ index }) => {
return (
<nav>
<Link
href={`/tv-shows/${index}`}
>
<div>
<h1>card click</h1>
</div>
</Link>
</nav>
);
};
export default Card;
Override index.js to remove unnecessary API calling code and take new card.js's props into account:
import Link from "next/link";
import Card from "../comps/card";
import Popular from "../comps/popular";
export default function IndexPage() {
return (
<div>
Hello World. {/* <Link href="/about">
<a>About</a>
</Link> */}
<Card index={0} img={Popular[0].img} />
<Card index={1} img={Popular[1].img} />
</div>
);
}
How the solution works is as follows:
We create dynamic routes which takes only query parameter index of our playlist. Every index parameter that is possible to be set is defined in paths: [{ params: { index: "0" } }, { params: { index: "1" } }]. These path params are then passed to our dynamic route which is then pre-rendered, and downloading all the data only once. And finally, our route displays data based on query parameters supplied by useRouter.
If I visit this code on local host, it is able to pull data from the API and then display it on a card.
import { formatNumber, parseTimestampJM } from '../../utils';
import { Card } from './UserTransactions.styled';
// STEP 1 : fetch data from api
export async function getStaticProps() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicmarket/v1/sales'
);
const data = await res.json();
return {
props: {
data,
},
};
}
function UserTransactionsComponent({ data }) {
const results = data;
console.log(results);
return (
<PageLayout>
<div>
<h1>This is a list of User Transactions!</h1>
</div>
<ul>
{results.data.map((result) => {
const {
sale_id,
buyer,
seller,
listing_price,
listing_symbol,
created_at_time,
} = result;
if (buyer !== null) {
return (
<Card>
<li key={sale_id}>
<h3>
{seller} just sold item number {sale_id} to {buyer} for{' '}
{formatNumber(listing_price)} {listing_symbol} at{' '}
{parseTimestampJM(created_at_time)}
</h3>
</li>
</Card>
);
}
})}
</ul>
</PageLayout>
);
}
export default UserTransactionsComponent;
When I create a component and then call it in to my index page like so:
<PageLayout>
<Banner modalType={MODAL_TYPES.CLAIM} />
<ExploreCard />
<HomepageStatistics />
<Title>New & Noteworthy</Title>
<UserTransactionsComponent />
<Grid items={featuredTemplates} />
</PageLayout>
);
};
export default MarketPlace;
it gives me the following error
TypeError: Cannot read properties of undefined (reading 'data')
27 | <ul>
> 28 | {results.data.map((result) => {
| ^
29 | const {
30 | sale_id,
31 | buyer,
I think that the reason I'm getting this error is because of the way the data is being fetched. Perhaps it's not being included in the component.
getStaticProps works only for page components inside pages folder. And the data is fetched at build time. If you wanna use UserTransactionsComponent as a normal component, you should use useEffect and make the API call on mount.
Here is what Next.js's documentation says about getStaticProps:
If you export a function called getStaticProps (Static Site Generation) from a page, Next.js will pre-render this page at build time using the props returned by getStaticProps.
Here is UserTransactionsComponent as a normal component:
import {useState, useEffect} from "react"
function UserTransactionsComponent() {
const [data, setData]=useState();
useEffect(()=>{
async function fetchData() {
const res = await fetch(
'https://proton.api.atomicassets.io/atomicmarket/v1/sales'
);
const {data} = await res.json();
setData(data)
}
fetchData()
},[]);
if(!data){
return (<div>Loading...</div>)
}
return (
<PageLayout>
<div>
<h1>This is a list of User Transactions!</h1>
</div>
<ul>
{data.map((result) => {
const {
sale_id,
buyer,
seller,
listing_price,
listing_symbol,
created_at_time,
} = result;
if (buyer !== null) {
return (
<Card>
<li key={sale_id}>
<h3>
{seller} just sold item number {sale_id} to {buyer} for{' '}
{formatNumber(listing_price)} {listing_symbol} at{' '}
{parseTimestampJM(created_at_time)}
</h3>
</li>
</Card>
);
}
})}
</ul>
</PageLayout>
);
}
export default UserTransactionsComponent;
I'm using image search and display app. Users can click on a photo and a modal would pop up. Those modal would have id in the url. However when I refresh the page, the modal isn't there and an error is shown. I get the url from unsplash api so with page refresh reload the url is gone. How do I Keep the url in url query so that the url persists even on page refresh?
Lisitem
import React, { useState } from "react";
import { Link, BrowserRouter as Router, Route } from "react-router-dom";
import ModalWrapper from "./ModalWrapper";
const ListItem = ({ photo }) => {
return (
<>
<Router>
<div key={photo.id} className="grid__item card">
<div className="card__body">
<Link to={{ pathname: `/${photo.id}`, state: photo }}>
<img src={photo.urls.small} alt="" />
</Link>
<Route path="/:photoId" component={ModalWrapper} />
</div>
</div>
</Router>
</>
);
};
export default ListItem;
Modal wrapper
import React from "react";
import Modal from "react-modal";
import { useHistory, useLocation } from "react-router-dom";
const customStyles = {
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)"
}
};
Modal.setAppElement("#root");
function ModalWrapper() {
const history = useHistory();
const location = useLocation();
const photo = location.state;
function downloadImage() {}
function close() {
history.push("/");
}
return (
<Modal isOpen={true} onRequestClose={close} style={customStyles}>
<img src={photo.urls.small} alt="" />
<div>
<button onClick={close} className="button">
Close
</button>
<button onClick={downloadImage()}>Download</button>
</div>
</Modal>
);
}
export default ModalWrapper;
The reason why it doesn't work when you refresh the page is because the photo that you passed as a param while navigating is no longer available. But, pathname is something that's still available (because it's part of the URL itself)
So, on the ModalWrapper page, you can check if photo is absent, then make a new API call to get the photo based on the id that is available in the pathname. I've never used unsplash API but I think it would be this API.
Your ModalWrapper would look like this
function ModalWrapper() {
const history = useHistory();
const location = useLocation();
const [photo, setPhoto] = useState(location.state);
useEffect(() => {
if (location.pathname && !location.state) {
// call the new API here using pathname (photo ID) and setPhoto
console.log(location.pathname);
}
}, [location]);
function downloadImage() {}
function close() {
history.push("/");
}
return (
!!photo && (
<Modal isOpen={true} onRequestClose={close} style={customStyles}>
<img src={photo.urls.small} alt="" />
<div>
<button onClick={close} className="button">
Close
</button>
<button onClick={downloadImage()}>Download</button>
</div>
</Modal>
)
);
}
You haven't asked this but, I would also move the Router and Route outside the ListItem and keep it in App.js (wrapping everything in there with Router). Keeping it in ListItem is like having a router and route for each list-item, which is not something you would ideally want. You would want to keep one router and route across the application, and it usually belongs to App.js or a wrapper or sorts. Here's the codesandbox after such changes
Summary / Issue
I've created an anime database app using Nextjs with deployment on Vercel. The build was fine and the initial page rendered, but only a few of my dynamic routes are being rendered, the rest display a 404 page. I went into the deploy log and found that for each dynamic page, only 10 routes were being built for every dynmaic route.
Deploy Screenshot from Vercel
While working in development (localhost:3000), there were no issues and everything ran fine.
The routes are based on the id of each title and there are thousands of titles.
My Code
Here is my code for one of the pages using getStaticPaths and getStaticProps
export const getStaticProps = async ({ params }) => {
const [anime, animeCharacters, categories, streaming, reviews] = await Promise.all([
fetch(`https://kitsu.io/api/edge/anime/${params.id}`),
fetch(`https://kitsu.io/api/edge/anime/${params.id}/characters`),
fetch(`https://kitsu.io/api/edge/anime/${params.id}/categories`),
fetch(`https://kitsu.io/api/edge/anime/${params.id}/streaming-links`),
fetch(`https://kitsu.io/api/edge/anime/${params.id}/reviews`),
])
.then((responses) =>
Promise.all(responses.map((response) => response.json()))
)
.catch((e) => console.log(e, "There was an error retrieving the data"))
return { props: { anime, animeCharacters, categories, streaming, reviews } }
}
export const getStaticPaths = async () => {
const res = await fetch("https://kitsu.io/api/edge/anime")
const anime = await res.json()
const paths = anime.data.map((show) => ({
params: { id: show.id },
}))
return { paths, fallback: false }
}
[id] is my dynamic route and as you can see, it's only being populated with 10 routes (the first 3 and 7 additional).
Despite the number of shows, I'm looping over each show and grabbing its ID and then passing that as the path.
What I've thought of
The API I'm using is the Kitsu API.
Within the docs, it states: "Resources are paginated in groups of 10 by default and can be increased to a maximum of 20". I figured this might be why 10 paths are being generated, but if that was the case, then why would it work fine in production and in deployment? Also, when I click on each poster image, it should bring me to that specific title by its id, whihc is dynamic, so it shouldn't matter how many recourses are being generated initially.
Code for dynamic page `/anime/[id]
import { useState } from "react"
import { useRouter } from 'next/router'
import fetch from "isomorphic-unfetch"
import formatedDates from "./../../helpers/formatDates"
import Navbar from "../../components/Navbar"
import TrailerVideo from "../../components/TrailerVideo"
import Characters from "./../../components/Characters"
import Categories from "../../components/Categories"
import Streamers from "../../components/Streamers"
import Reviews from "../../components/Reviews"
const Post = ({ anime, animeCharacters, categories, streaming, reviews}) => {
const [readMore, setReadMore] = useState(false)
const handleReadMore = () => setReadMore((prevState) => !prevState)
let {
titles: { en, ja_jp },
synopsis,
startDate,
endDate,
ageRating,
ageRatingGuide,
averageRating,
episodeCount,
posterImage: { small },
coverImage,
youtubeVideoId,
} = anime.data.attributes
const defaultImg = "/cover-img-default.jpg"
const synopsisSubString = () =>
!readMore ? synopsis.substring(0, 240) : synopsis.substring(0, 2000)
const router = useRouter()
if(router.isFallback) return <div>loading...</div>
return (
<div className='relative'>
<div className='z-0'>
<img
className='absolute mb-4 h-12 min-h-230 w-full object-cover opacity-50'
src={!coverImage ? defaultImg : coverImage.large}
/>
</div>
<div className='relative container z-50'>
<Navbar />
<div className='mt-16 flex flex-wrap md:flex-no-wrap'>
{/* Main */}
<div className='md:max-w-284'>
<img className='z-50 mb-6' src={small} />
<div className='xl:text-lg pb-6'>
<h1 className='mb-2'>Anime Details</h1>
<ul>
<li>
<span className='font-bold'>Japanese Title:</span> {ja_jp}
</li>
<li>
<span className='font-bold'>Aired:</span>{" "}
{formatedDates(startDate, endDate)}
</li>
<li>
<span className='font-bold'>Rating:</span> {ageRating} /{" "}
{ageRatingGuide}
</li>
<li>
<span className='font-bold'>Episodes:</span> {episodeCount}
</li>
</ul>
</div>
<Streamers streaming={streaming} />
</div>
{/* Info Section */}
<div className='flex flex-wrap lg:flex-no-wrap md:flex-1 '>
<div className='mt-6 md:mt-40 md:ml-6 lg:mr-10'>
<h1 className='sm:text-3xl pb-1'>{en}</h1>
<h2 className='sm:text-xl lg:text-2xl pb-4 text-yellow-600'>
{averageRating}{" "}
<span className='text-white text-base lg:text-lg'>
Community Rating
</span>
</h2>
<div>
<p className='max-w-2xl pb-3 overflow-hidden xl:text-lg'>
{synopsisSubString()}
<span className={!readMore ? "inline" : "hidden"}>...</span>
</p>
<button
className='text-teal-500 hover:text-teal-900 transition ease-in-out duration-500 focus:outline-none focus:shadow-outline'
onClick={handleReadMore}
>
{!readMore ? "Read More" : "Read Less"}
</button>
</div>
<Categories categories={categories} />
<Reviews reviews={reviews}/>
</div>
{/* Sidebar */}
<section className='lg:max-w-sm mt-10 md:ml-6 lg:ml-0'>
<TrailerVideo youtubeVideoId={youtubeVideoId} />
<Characters animeCharacters={animeCharacters} />
</section>
</div>
</div>
</div>
</div>
)
}
export const getStaticProps = async ({ params }) => {
const [anime, animeCharacters, categories, streaming, reviews] = await Promise.all([
fetch(`https://kitsu.io/api/edge/anime/${params.id}`),
fetch(`https://kitsu.io/api/edge/anime/${params.id}/characters`),
fetch(`https://kitsu.io/api/edge/anime/${params.id}/categories`),
fetch(`https://kitsu.io/api/edge/anime/${params.id}/streaming-links`),
fetch(`https://kitsu.io/api/edge/anime/${params.id}/reviews`),
])
.then((responses) =>
Promise.all(responses.map((response) => response.json()))
)
.catch((e) => console.log(e, "There was an error retrieving the data"))
return { props: { anime, animeCharacters, categories, streaming, reviews } }
}
export const getStaticPaths = async () => {
const res = await fetch("https://kitsu.io/api/edge/anime")
const anime = await res.json()
const paths = anime.data.map((show) => ({
params: { id: show.id },
}))
return { paths, fallback: true }
}
export default Post
Screenshot of Errror
Repo
If the API you are working with serves resources in groups of 10, then when you call the API in getStaticPaths you only have 10 id ahead of time. Using static generation in nextjs builds static pages for all the available ids ahead of time in production mode. But when in development mode your server will recreate each page on per request basis. So to solve this problem you can build the first 10 pages and make the rest of the pages to be fallback. Here is how you do it.
export const getStaticPaths = async () => {
const res = await fetch("https://kitsu.io/api/edge/anime")
const anime = await res.json()
// you can make a series of calls to the API requesting
// the next page to get the desired amount of data (100 or 1000)
// how many ever static pages you want to build ahead of time
const paths = anime.data.map((show) => ({
params: { id: show.id },
}))
// this will generate 10(resource limit if you make 1 call because your API returns only 10 resources)
// pages ahead of time and rest of the pages will be fallback
return { paths, fallback: true }
}
Keep in mind when using {fallback: true} in getStaticPaths you need have some sort of loading indicator because the page will be statically generated when you make a request for the first time which will take some time(usually very fast).
In your page that you want to statically generate
function MyPage = (props) {
const router = useRouter()
if (router.isFallback) {
// your loading indicator
return <div>loading...</div>
}
return (
// the normal logic for your page
)
}
P.S. I forgot to mention how to handle errors where API responds with 404 or 500 and the resource doesn't exist to send as props when using fallback in static generation.
So here's how to do it.
const getStaticProps = async () => {
// your data fetching logic
// if fail
return {
props: {data: null, error: true, statusCode: 'the-status-code-you-want-to-send-(500 or 404)'}
}
// if success
return {
props: {data: 'my-fetched-data', error: false}
}
}
// in the page component
import ErrorPage from 'next/error';
function MyStaticPage(props) {
if (props.error) {
return <ErrorPage statusCode={404}/>
}
// else you normal page logic
}
Let me know if it helped or you encountered some error while implementing.
Here is where you can learn more https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation
I am trying to render the main entry point of my application when an auth status change occurs but when I do try to go to the main entry point of my application I get a blank screen. I'm assuming I can't go to a main page if it isn't being called within the render function itself? If so, how do I render the main Page of my app when I established signIn?
index.js
class App extends Component {
/*
constructor(props) {
super(props);
this.state = {
authState: null,
authData: null
}
}
*/
handleAuthStateChange(state) {
alert(state)
if (state === 'signedIn') {
alert('here');
return ( // go To Entry point of app
<ApolloProvider store={store} client={client}>
<AppWithNavigationState/>
</ApolloProvider>
);
}
}
render() {
//const { authState, authData } = this.state;
// const signedIn = (authState === 'signedIn');
return (
<Authenticator hideDefault={true} onStateChange={this.handleAuthStateChange}>
<Login/>
</Authenticator>
);
}
}
export default App = codePush(App);
Login.js
export default class Login extends Component {
render() {
const { authState, onStateChange } = this.props;
if (!['signIn', 'signedOut', 'signedUp'].includes(authState)) {
alert('login')
return null;
}
return (<View style={styles.container}>
<View style={styles.backgroundContainer}>
<Image source={images.icons.LoginImage} style={styles.backgroundImage} />
</View>
<View style={styles.overlay}>
<Button iconLeft light rounded style={styles.facebookLoginButton}>
<Icon style={styles.facebookIcon} name='logo-facebook' />
<Text style={styles.facebookButtonText}>Login with Facebook</Text>
</Button>
<Button rounded bordered light style={styles.loginLaterButton}
onPress={() => onStateChange('signedIn')}>
<Text style={styles.buttonText}>Sign In Test</Text>
</Button>
</View>
</View>
);
}
}
If you resolved it I hope it will help someone else.
I think the following is a better option than using 'onAuthStateChange':
from Amplify dox :
import { Auth } from 'aws-amplify';
Auth.currentAuthenticatedUser({
bypassCache: false // Optional, By default is false. If set to true, this call will send a request to Cognito to get the latest user data
}).then(user => console.log(user))
.catch(err => console.log(err));
You can add your logic within '.then(user => ...' to add route to your protected pages. Also you can redirect to Login page from '.catch(err => '.
If you include above code within a function and call it from 'componentWillReceiveProps' it should be called every time auth status changes.
This is really about rendering and state (and not anything to do with AWS Amplify). First, set up state in your constructor:
constructor(props) {
super(props);
this.state = { authState: '' };
}
Then, your onAuthStateChange() becomes:
onAuthStateChange(newState) {
if (newState === 'signedIn') {
this.setState({ authState: newState });
}
}
Finally, in your render() method, you adjust your rendering so that it does "the right thing" based on your auth state.
render() {
if (this.state.authState === 'signedIn') {
return (<ApolloProvider ...><MyApp/></ApolloProvider>);
} else {
return (<Authenticator .../>);
}
}
You can abstract this away with a HOC as well (the same way the withAuthenticator() HOC from AWS Amplify does it). Basically, the Authenticator gets displayed initially. Once the signedIn state is received, the internal component state is updated and that causes a re-render of the component with the rest of your app.