So basically sometimes when I scroll between pages with React-router and I go try to go to a page with async code that runs as the lifecycle runs (componentDidMount) it throws me an error saying you can't map a state of 'null'.
question is.. how can I prevent that from happening when navigating between pages?
Example (Dashboard)
class Dashboard extends Component {
componentDidMount() {
this.props.fetchMovies();
this.props.fetchSeries();
}
handleFetchMovies() {
if(this.props.assets.fetchMoviesError) {
return (
<div className="loading">
<h1>{this.props.assets.fetchMoviesError}</h1>
</div>
);
} else if(this.props.assets.loadingMovies) {
return (
<div className="loading">
<h1>Loading...</h1>
</div>
);
} else {
return this.props.assets.latestMovies.map(movie => {
const movieURL = movie.title.split(' ').join('+').toLowerCase().trim();
return (
<div className="latest" key={movie._id}>
<Link to={`/movie/${movieURL}`}>
<div className="asset-cover" style={{ backgroundImage: `url(${movie.poster})` }}></div>
</Link>
<h1><Link to={`/movie/${movieURL}`}>{movie.title}</Link></h1>
<Link to={`/sort/years/${movie.year }`}>{movie.year}</Link>
</div>
)
}
)
}
}
whenever I go from this page to a different page and try to go back to it it says
this.props.assets.latestMovies.map is not a function
because the componentDidMount function didn't run.
Related
I've got a strange issue, and honestly have no idea where to even begin looking.
I have a NextJS app with a GraphQL endpoint built into the pages/api section. It's still pretty basic, I mostly just have login/register functionality built out.
I noticed an issue where my currentUser query was being run on pages that should not be running it (like /login and /register). What I discovered is that on my Page component wrapper is that the _error route is being triggered for some reason, even though it's not displaying that client-side. This component simply wraps the main in _app.tsx.
Here's the Page component wrapper:
/* eslint-disable #typescript-eslint/no-explicit-any */
// third-party
import React, { ReactChild, ReactChildren } from 'react';
import { useRouter } from 'next/router';
import { AnimateSharedLayout } from 'framer-motion';
// custom components
import CustomDrawer from 'components/common/Drawer';
import Navbar from 'components/common/Navbar';
// styles
import CommonPageStyles from './CommonPageStyles';
interface AuxProps {
children: ReactChild | ReactChildren;
}
const Page: React.FC = ({ children }: AuxProps) => {
const router = useRouter();
console.log(router.route);
const isLoggedOut =
router.route === '/_error' || // temporary fix
router.route === '/login' ||
router.route === '/register' ||
router.route === '/terms-and-conditions';
const isOnboarding = router.route === '/onboarding';
const renderPage = () => {
console.log({ isLoggedOut });
console.log({ isOnboarding });
if (isLoggedOut && !isOnboarding) {
return (
<div style={{ padding: '0', height: '100%', width: '100%' }}>
{children}
</div>
);
} else if (!isLoggedOut && isOnboarding) {
return (
<div style={{ padding: '64px 0 0 0', height: '100%', width: '100%' }}>
<Navbar simple />
{children}
</div>
);
} else {
return (
<div className="page-container">
<Navbar />
<CustomDrawer />
<div className="inner-wrap">{children}</div>
</div>
);
}
};
return (
<AnimateSharedLayout>
<CommonPageStyles>{renderPage()}</CommonPageStyles>
</AnimateSharedLayout>
);
};
export default Page;
I have three console logs placed in this component: one for router.route, and one for my custom variables isLoggedIn and isOnboarding. When I refresh the /login page, here is what I see on my server:
/login
{ isLoggedOut: true }
{ isOnboarding: false }
/_error
{ isLoggedOut: true }
{ isOnboarding: false }
but the client is rendering the Login page just as normal.
So I built a crude custom _error page to try and debug this:
function Error({ statusCode }) {
return (
<p>
{statusCode
? `An error ${statusCode} occurred on server`
: 'An error occurred on client'}
</p>
);
}
Error.getInitialProps = ({ res, err }) => {
const statusCode = res ? res.statusCode : err ? err.statusCode : 404;
console.log({ statusCode });
return { statusCode };
};
export default Error;
and now I see this on the server:
{ isLoggedOut: true }
{ isOnboarding: false }
event - build page: /_error
wait - compiling...
event - compiled successfully
{ statusCode: 404 }
/_error
{ isLoggedOut: true }
{ isOnboarding: false }
As you can see the error statusCode that is being returned is a 404, but I have no idea why! Does anyone have any suggestions as to why this might be happening?
Figured it out!
I'd forgotten that I had installed the next-pwa package when initially building out this project. This generates a manifest.json, which is referenced in the of _document.tsx. This file was not being built, resulting in a 404. I simply removed this package and any references to it, which appears to have fixed the issue!
I made a website with NextJS and deployed it on Vercel.
From url: https://etiselektrik.vercel.app/teknikbilgi/ if i click the first article. The page loads without any problem.
However if i directly enter the url with the slug page: https://etiselektrik.vercel.app/teknikbilgi/reaktifenerji-reaktifceza I get 500 internal server error.
I dont have this error in my production build.
Here is my code for the slug page:
import ReactHtmlParser from 'react-html-parser';
import Layout from '../../components/Layout';
import { API_URL } from '../../config/index';
export default function BlogPost({ post }) {
return (
<Layout
title={post.title}
description={post.metaDescription}
keywords={post.metaKeywords}
>
<h1 className='px-4 py-4 md: my-4 simpleContainer font-semibold text-2xl text-secondary text-left'>
{post.title}
</h1>
<div className='px-4 simpleContainer text-secondary'>
{ReactHtmlParser(post.content)}
</div>
</Layout>
);
}
export async function getServerSideProps({ query: { slug } }) {
try {
const res = await fetch(`${API_URL}/teknikbilgis/${slug}`);
const post = await res.json();
return {
props: {
post: post,
},
};
} catch {
return {
props: {
post: {},
},
};
}
}
I found the issue. On the client side, i used .replaceAll function to change my post before parsing. (I didnt add them in my code as i thought it was irrelevant)
.replaceAll() seems to cause problems in nextjs on deployment. I changed it to .replace() Now it works.
I am trying to redirect to a protected page in the following way:
<button
onClick={() => {
router.push("/welcome");
}}
>
The welcome page is protected in the following way:
import { useSession, signOut } from "next-auth/client";
import { useRouter } from "next/router";
import { useEffect } from "react";
export default function Welcome() {
const [session, loading] = useSession();
const router = useRouter();
useEffect(() => {
if (!loading && !session?.accessToken) {
router.push("/auth/login");
}
}, [loading, session]);
if (!session) {
return <div></div>;
}
return (
<div>
<h1>Welcome Page</h1>
</div>
);
}
The welcome page works fine if I try to access it directly.
But if I use router.push(), it takes seconds to load. It mostly happens after going back from the welcome page with the browser's back button and then pressing the button again. Though it sometimes happens anyways.
What am I doing wrong? I was told it's normal behaviour for a dev environment, but it happens in production build too.
Here is a reproduction:
https://codesandbox.io/s/distracted-browser-fyjek
I solved the issue by wrapping my Component in _app.tsx with Provider like so:
<Provider session={pageProps.session}>
<Component {...pageProps} />
</Provider>;
and adding
export async function getServerSideProps(context: NextPageContext) {
const session = await getSession(context);
return {
props: { session },
};
}
to server side rendered pages like explained here.
I am pretty new to next.js and kind of stuck on basic issue.
here is my _app.js
class MyApp extends App {
constructor (props) {
super(props)
this.persistor = persistStore(props.store)
}
static async getInitialProps ({ Component, ctx }) {
let pageProps = {}
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps({ ctx })
}
return { pageProps }
}
render () {
const { Component, pageProps, store } = this.props
return (
<Provider store={store}>
<PersistGate
loading={null}
persistor={this.persistor}
>
<Component {...pageProps} />
</PersistGate>
</Provider>
)
}
}
export default withRedux(createStore)(withReduxSaga(MyApp))
This is my index.js
function Home (props) {
return (
<div>
<div className='home'>
<Link href='/login'>
<a>Go to Login</a>
</Link>
<h1>THIS IS HOME</h1>
</div>
}
Currently when I start an app it lands on index page.
How do I make this land on Login page directly?
Let me know if you need to see other files?
not sure if this is the right way of doing things but I was able to navigate user on Login page instead of index page when website loads.
// redirect user to login page
useEffect(() => {
Router.push('/login')
})
return (
<div>
</div>
)
Cliffs:
I'm trying to pre-fill forms from a user's previous entries, which is all stored in a MongoDB colelction I do this doing conventional Javascript:
componentDidMount(){
let name = document.getElementById("name");
name.value = this.props.iData.name;
}
This works fine unless I refresh the page, in which case I get an error that this.props.iData is undefined. So, whenever I visit the page with the pre-filled data, it works fine and subscription works fine. But when I refresh that same page, the subscription doesn't load fast enough.
The subscription is done like this:
export default createContainer(function(){
const subHandle = Meteor.subscribe("iData", function(){
});
const isLoading = !subHandle.ready();
return {
isLoading,
iData: Poll.find().fetch(),
}
}, UserSettings)
I must be doing something wrong for this to happen the way it's happening.
Did you make sure that your data is ready when componentDidMount is getting called. And also you shouldnt use document.getElementById() in react.
In react you need to use refs.
class Sample extends React.Component {
componentDidMount() {
this.ref.input.value = 'some value';
}
render() {
return (
<input ref="input" />
);
}
}
You can also do this:
class Sample extends React.Component {
render() {
return (
<div>
{!this.props.isLoading ? (
<input value = {this.props.someValue}/>
) : (
<p>Loading </p>
)}
</div>
);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>