react-loadable component does not wait trigger to load - javascript

This is a basic header component that includes buttons to show login/register forms on click.
The intention is, of course, that the login and register components shouldn't be loaded until requested.
react-loadable is creating an additional file ( 0.js ) that seem to include the register component, but my search did not turn out any reference to login.
In any case, upon initial load, both login and register are being loaded, as my console.log shows.
Of course, I was expecting that they would not be loaded until the triggering button was clicked on.
Note that I did attempt to use react-loadable on routes, and it seems to work correctly, ie, I can see the files being loaded on the network tab of the dev tools.
Also, I happen to have a service worker precaching the build files for now, but I do not believe that should impact this. Or should it? It doesn't on routes.
Excerpts of the header component:
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { withRouter } from 'react-router-dom';
import Loadable from 'react-loadable';
//import Register from '../register/'; ** PREVIOUS REGULAR IMPORT
//import Login from '../login/'; ** PREVIOUS REGULAR IMPORT
import { Loading } from '../../../tools/functions';
const Login = Loadable({
loader: () => import('../login/'),
loading: Loading,
delay: 200,
timeout: 5000
});
const Register = Loadable({
loader: () => import('../register/'),
loading: Loading,
delay: 200,
timeout: 10000
});
render () {
return (
<header>
<div className="header_top flex">
<div className="branding"></div>
<div className="header_spacer"></div>
<Status handleClick={this.handleLoginRegisterClick}/>
</div>
<Nav />
<div className="auth">
<Register
active={this.state.activeRegister}
handleClick={this.handleLoginRegisterClick}
toggleDialog={this.toggleLoginRegisterDialogs}
/>
<Login
active={this.state.activeLogin}
handleClick={this.handleLoginRegisterClick}
handleClickPasswordReset={this.togglePasswordResetRequest}
toggleDialog={this.toggleLoginRegisterDialogs}
/>
<PasswordResetRequest
active={this.state.activePasswordResetRequest}
hidePasswordResetRequest={this.togglePasswordResetRequest}
/>
<SessionHandler location={location}/>
</div>
{showAdForm()}
</header>
);
}
The Loading function:
export const Loading = ({ error }) => {
if (error) {
return 'error';
} else {
return <h3>Loading...</h3>;
}
}

My mistake was that I had the state change on the child component.
So, I factored it up, changing the render method of the header component as follows:
const activeDialog = () => {
if (this.state.activeLogin) {
return (
<Login
active={this.state.activeLogin}
handleClick={this.handleLoginRegisterClick}
handleClickPasswordReset={this.togglePasswordResetRequest}
toggleDialog={this.toggleLoginRegisterDialogs}
/>
)
} else if (this.state.activeRegister) {
return (
<Register
active={this.state.activeRegister}
handleClick={this.handleLoginRegisterClick}
toggleDialog={this.toggleLoginRegisterDialogs}
/>
)
} else if (this.state.activePasswordResetRequest) {
return (
<PasswordResetRequest
active={this.state.activePasswordResetRequest}
hidePasswordResetRequest={this.togglePasswordResetRequest}
/>
)
}
}
return (
<header>
<div className="header_top flex">
<div className="branding"></div>
<div className="header_spacer"></div>
<Status handleClick={this.handleLoginRegisterClick}/>
</div>
<Nav />
<div className="auth">
{activeDialog()}
<SessionHandler location={location}/>
</div>
{showAdForm()}
</header>
);

Related

Loading page on nextjs 13

Hi im trying to get a loading page to show while website is taking the time to load. as it quite a large website I thought a loading screen would provide the best possible user experience however I cannot seem to figure out how to get it to work on nextjs 13. I have created a simple functional component that says loading... and have imported it directly into my layout.jsx folder.
I am using the app directory method which is quite new and im also new at nextjs so im a little lost ^^
I imagine I might need to set state at some point but I cant seem to figure out when and where to do it
any advice would be great.
thanks
import "./globals.css";
import React, { useState, useEffect } from "react";
import Loading from "../components/loading/loading";
const Layout = ({ children, dataLoaded }) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
if (dataLoaded) {
setLoading(false);
}
}, [dataLoaded]);
return (
<body className="app {oswald.className}">
{loading && <Loading />}
{children}
</body>
);
};
export default Layout;
.
.
.
Attempt 1 -
After following one of the answers below it does not seem like my loading page is showing up at all. and no errors showing up.
my layout is as follows
layout.jsx
import "./globals.css";
import { Suspense } from "react";
import Loading from "../components/loading/loading";
export default function RootLayout({ children }) {
return (
<html lang="en">
<head />
<body>
<Suspense fallback={<Loading />}>{children}</Suspense>
</body>
</html>
);
}
LoadingPage.js
const LoadingPage = () => {
return (
<div className="loading w-screen h-screen bg-red-100">
<p>Loading...</p>
</div>
);
};
export default LoadingPage;
Loading.js
import LoadingPage from "#/components/loading/loading";
export default function Loading() {
return <LoadingPage />;
}
In NextJS 13, there's actually a default way to handle loading states within pages. You can declare a loading.tsx file in your /app directory, with this content:
export default function Loading() {
return <Loading />
}
Then, inside your Layout, you can wrap your page with a Suspense tag, like this:
<Layout>
<Navbar>
...
<Suspense fallback={<Loading/>}>
<Page/>
</Suspense>
</Layout>
Your loading state will be automatically handled upon navigation.

NextJS: what is the proper way to handle dynamic route pages?

I'm working on a project in NextJS for the first time, and I'm wondering what the right way to handle dynamic routing is.
I have a http://localhost:3000/trips route which displays a page with list of cards, each of which represent a "trip":
When I tap on one of these cards, I want to navigate to a dynamic page for that route such as http://localhost:3000/trips/0b68a50a-8377-4720-94b4-fabdabc12da1
This is my folder structure:
As you can see, I already have the dynamic routes set up and it is working.
TripCard is the card component. TripComponent is a grid of TripCards. trips/index.tsx contains the TripsComponent (and other UI).
Currently I'm handling the dynamic route in TripCard as:
import { Trip } from './Models'
import { useRouter } from 'next/router'
const TripCard = ({ trip }: { trip: Trip }) => {
const router = useRouter()
return (
<div className="card bg-base-100 shadow-xl hover:bg-gray-100 active:bg-gray-300">
<div className="card-body" onClick={() => router.push('/trips/' + trip.id)}>
<h2 className="card-title">{trip.name}</h2>
<p>This is a trip!</p>
</div>
</div>
)
}
export default TripCard
And the dynamic page [tripId].tsx looks like:
import { NextPage } from 'next'
import { useRouter } from 'next/router'
const TripPage: NextPage = () => {
const router = useRouter()
const tripId = router.query.tripId
return (
<div>
<h1>This is {tripId}</h1>
</div>
)
}
export default TripPage
And TripsComponent.tsx:
import { Trip } from './Models'
import TripCard from './TripCard'
const TripsComponent = ({ trips }: { trips: Trip[] }) => {
return (
<div>
<div className="grid grid-cols-4 gap-4">
{trips.map((trip: Trip) => (
<div>
<TripCard trip={trip}></TripCard>
</div>
))}
</div>
</div>
)
}
export default TripsComponent
And trips/index.tsx:
import axios from 'axios'
import { GetStaticProps, InferGetStaticPropsType, NextPage } from 'next'
import { Trip } from '../../components/Models'
import TripsComponent from '../../components/TripsComponent'
const TripsPage: NextPage = (props: InferGetStaticPropsType<typeof getStaticProps>) => {
return (
<div className="m-9">
<h1 className="mt-9 text-3xl font-bold text-slate-800">Your Trips</h1>
<div className="justify-end">
<button className="btn btn-primary">Add Trip</button>
</div>
<div className="divider"></div>
<TripsComponent trips={props.data} />
</div>
)
}
export const getStaticProps: GetStaticProps = async () => {
// fetches data and passes to props
}
export default TripsPage
I guess my question is, what is the proper way to do routing like this where I have cards, and each card will go to a dynamic URL with an associated page? In TripCard I have a hardcoded router.push:
<div className="card-body" onClick={() => router.push('/trips/' + trip.id)}>
But that doesn't seem like the right way to handle this. For example, what if I want to use TripCard in another view and go to another route?
What's the best way to structure code that performs this function in NextJS?
Maybe you want to pass the url as props and use NextJs <Link/>:
import { Trip } from './Models'
import { useRouter } from 'next/router'
import Link from 'next/link'
const TripCard = ({ trip, url }: { trip: Trip, url: string }) => {
const router = useRouter()
return (
<div className="card bg-base-100 shadow-xl hover:bg-gray-100 active:bg-gray-300">
<Link href={url} passHref>
<div className="card-body">
<h2 className="card-title">{trip.name}</h2>
<p>This is a trip!</p>
</div>
</Link>
</div>
)
}
export default TripCard
And then use the component like this:
<TripCard trip={trip} href={'/trips/' + trip.id}></TripCard>
Hope this help.
For dynamic routes, you will have to use getStaticPaths and getStaticProps to fetch the paths before and generate all the paths. When you export getStaticPaths from the page Nextjs will pre-render all the paths specified by getStaticPaths.
getStaicPaths has to return two things paths and fallback. paths property will have to be an array of params. And fallback means should Nextjs look for pages and pre-render if not pre-rendered before. Suppose after publishing the site you created a new card. So, If the fallback is true NextJs will look for the card in the database and pre-render then store it in the file system. If fallback is false it will show 404 not found.
Also Instead of using router.push use Link component.
https://nextjs.org/docs/api-reference/data-fetching/get-static-paths
The routing you have implemented is correct only. Maybe you can try like this
For trips, you can navigate to /trips and for each trip /trip/<trip id>
If you want to make the trip URL dynamic then you can pass the URL itself to TripCard component.
url will be <route name>/<trip id>
<TripCard trip={trip} url={url}></TripCard>

react/no-multi-comp is showing up as warnings

I am using hook router 1.2.5 and I have a very simple home page as below:
import { useRoutes } from "hookrouter";
import React from "react";
import Nav from "./pages/Nav";
import AboutPage from "./pages/About";
const HomePage = () => {
const routeResult = useRoutes({
"/about": () => <AboutPage />
});
return (
<div fluid>
<div xs={3} md={1} lg={1} className="nav-container">
<Nav />
</div>
<div xs={9} md={11} lg={11}>
{routeResult || <AboutPage />}
</div>
</div>
);
};
export default HomePage;
But when I run lint, I see below warnings show up.
8:10 warning Component definition is missing display name react/display-name
8:10 warning Declare only one React component per file react/no-multi-comp
I know I can disable these eslint warnings. But I would like to know how to fix them. For example, I don't have another component in my file. So why would it show react/no-multi-comp warning, or did I miss something? Any helps are appreciated.
UPDATE
I was able to fix react/display-name by replacing the arrow function as below:
const routeResult = useRoutes({
"/about"() {
return <AboutPage />;
}
});

Next js 404 page not working in tsx extension

I have created a next js project with normal javaScript.This is my 404 page
404.js
import { useEffect } from "react";
import { useRouter } from "next/router";
import Link from "next/link";
export default function NotFound() {
const router = useRouter();
useEffect(() => {
setTimeout(() => {
// router.go(1)
router.push("/");
}, 3000);
}, []);
return (
<div className="not-found">
<h1>Oooops!!!</h1>
<p>
Go back to home page
<Link href="/">
<a>Home page</a>
</Link>
</p>
</div>
);
}
Then I created a seperate type script based next js project.
404.tsx
// import type { NextPage } from 'next'
import Link from "next/link";
const NotFound = () => {
return (
<div>
<h2>Oh no!</h2>
<p>There is not much left here for you</p>
{/* <Link href="/">
<a >Visit our home page</a>
</Link> */}
</div>
);
};
export default NotFound;
This is what I get in the typescript project.
What is wrong in the 404.tsx file?
It says that client_dev_noop_js module loading failed. There is nothing with 404.tsx, however, it is better to name _error.js and _error.tsx

How i do a push properly?

I'm making a login page in react. When the login is done my react needs to redirect to my page FrontFeed. I'am using this.props.history.push('/feed'); to push the page, but when acess the login page the and put my credentials the login redirect to the login with diferent url and i need to make login again, and only the second time the login redirect to the FrontFeed.
When i access the page for the first time
I made a login (The credentials are right) and the page me redirect to the same page but a diferent url /#/ -> /?#/. The funtions are not called because the requisitions aren't made, and the local storage don't save nothing.
But the second time i made login, everything works fine, the requisitions are maden and the local storage is stored, and my page is redirect to http://localhost:3000/?#/feed.
I'm not sure if i'm using the react-router-dom right. But there's something wrong.
import React from 'react';
import './css/App.css';
import MainPage from './pages/MainPage.js';
function App() {
return (
<MainPage/>
);
}
export default App;
import React, { Component } from 'react';
import '../css/MainPage.css';
import FrontFeed from './FrontFeed.js';
import Login from './Login.js';
import {
Route,
NavLink,
HashRouter
} from "react-router-dom";
export default class MainPage extends Component{
componentDidMount() {
var token = localStorage.getItem('Token');
console.log(token);
}
render () {
return (
<HashRouter>
<div className="app">
<div className="content">
<Route exact path="/" component={Login}/>
<Route path="/feed" component={FrontFeed}/>
</div>
</div>
</HashRouter>
);
}
}
import React, { Component } from 'react';
import '../css/Login.css';
import UserService from '../services/UserService'
import {ToastsContainer, ToastsContainerPosition, ToastsStore} from 'react-toasts';
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
user: new UserService(),
password: '',
username: '',
}
}
componentDidMount() {
var token = localStorage.getItem('Token');
if(token) {
this.props.history.push('/feed');
}
}
handleChangeUsername(event) {
this.setState({username: event.target.value});
}
handleChangePassword(event) {
this.setState({password: event.target.value});
}
handleSubmit(event) {
this.state.user.login(this.state.username, this.state.password).then(res => {
if (res.data.super) {
localStorage.setItem('Token', res.data.key);
ToastsStore.success('Login Sucess!');
} else {
ToastsStore.success("You aren't super!");
}
this.props.history.push('/feed');
}).catch(err => {
ToastsStore.success('Login Failed');
})
}
render() {
return (
<div className="loginContent">
<ToastsContainer position={ToastsContainerPosition.BOTTOM_CENTER}
store={ToastsStore}/>
<div className="title">Login to ADM</div>
<div className="credentialsContainer">
<form onSubmit={e => this.handleSubmit(e)}>
<div className="username">
Username:
<div className="inputUsername">
<input type="text"
value={this.state.username}
onChange={e => this.handleChangeUsername(e)} />
</div>
</div>
<div className="password">
Password:
<div className="inputPassword">
<input type="password"
value={this.state.password}
onChange={e => this.handleChangePassword(e)} />
</div>
</div>
<div className="submitButton">
<input type="submit" value="Search" />
</div>
</form>
</div>
</div>
);
}
}
put event.preventDefault(); inside of the handleSubmit function, it prevents the page to reload so this is why your functions are not getting executed on the first time
a typical form will send data to the action with a method
<form action="/action_page.php" method="post">
but since you are using the handleSubmit() and not an action then it will send data to no where (you can read more about forms here https://www.w3schools.com/html/html_forms.asp )
when you submit the first time, you will see in the url the input values that you have in the form (if you put names on them) but since you didn't put names on them in your form thus you don't see them in the url (assuming from the code that you provided above)
event.preventDefault() prevents the usual form submit and the page reload so you can execute the functions inside of it (same thing I have did to a project I worked on)
after you put event.preventDefault(); to the form, the login will work from the first attempt and react router will redirect it to the /feed page
hope my answer helped a bit

Categories

Resources