I've created a very simple Next.js-project with two pages.
Both pages include a basic layout component:
// Page Component
render() {
return (
<PageLayout>
This is page A
</PageLayout>
);
}
And PageLayout looks something like this:
// PageLayout
render() {
return (
<div>
<Header />
{this.props.children}
</div>
);
}
So both pages use PageLayout to render a basic template that also includes the same Header on both pages.
My problem is that the Header component is re-created when navigating between the two pages. I think this is bad not only from a performance point of view, but also because in this case all DOM-Nodes and all React components loose their local state.
So I would like to know if there is something I am missing or how we can create shared components between pages that are reused properly (at least if their state did not change, of course).
You have Two pages with common component:
Page A:
<A>
<Common />
</A>
Page B:
<B>
<Common />
</B>
From the React documentation:
Whenever the root elements have different types, React will tear down
the old tree and build the new tree from scratch. Going from <a> to
<img>, or from <Article> to <Comment>, or from <Button> to <div> - any
of those will lead to a full rebuild.
This is why you lose the state in Common (Header) component. Like I suggested in the comment you would have to use an external state like redux.
You have to create a component with the name of layout
// components/layout.js
import Navbar from './navbar'
import Footer from './footer'
export default function Layout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
And then rap your _app.js component like this
// pages/_app.js
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
for more read https://nextjs.org/docs/basic-features/layouts
Related
so I have a react movie website which fetches data from tmbd , on home page it shows all trending movies data , and on the favorites page it shows all favorite movies , i routed these two pages but when i try to access favorites my browser gets really slow and tells me to stop the webpage , only after reloading the page , i get redirected to favorites page any solution in this ?
this is my nav bar where the links are , when i try to access the favourites page its shows that the page is slowing down
import React, { Component } from 'react'
import { Link } from "react-router-dom";
export default class Navbar extends Component {
render() {
return (
<div style={{display:'flex', padding:'0.5'}}>
<Link to="/" style={{textDecoration:'none'}}>
<h1 style={{marginTop:'1rem',marginLeft:'1rem'}}>Movies App</h1>
</Link>
<Link to='/favourites' style={{textDecoration:'none'}}>
<h2 style={{marginLeft :'5rem',marginTop:'2rem'}}>Favorites</h2>
</Link>
</div>
)
}
}
Here is my APP.js
import logo from './logo.svg';
import './App.css';
import React, { Component } from 'react';
import Navbar from './Components/Navbar';
import Banner from './Components/Banner';
import Movies from './Components/Movies';
import Favourite from './Components/Favourite';
import {Switch,Route, BrowserRouter,Routes} from 'react-router-dom';
function App() {
return (
// <>
// <Navbar></Navbar>
// <Banner></Banner>
// <Movies></Movies>
// </>
<BrowserRouter>
<Navbar/>
<Routes>
<Route exact path="/" element={<HomePage/>}/>
<Route exact path="/favourites" element={<Favourite/>}/>
</Routes>
</BrowserRouter>
);
}
function HomePage() {
return (
<>
<Banner />
<Movies />
</>
);
}
export default App;
when i try to access the individual file without routing it works fine ,but when i try to access this with routing my browser starts to slow down and the data isnt fetched untill i reload the page
my Github repo link: https://github.com/faizxmohammad/React-MovieApp/tree/master/src
I am expecting to fetch data of favorites pages without slowing down the browser
I cloned your code.
The problem is in the file Favorites.js, line 45. You are changing the state, inside the render method. This causes loops. Move any state change, outside render method. Why? This is the workflow:
State change -> renders the component
You are changing your state, inside render component, so it becomes:
state change -> render (state change inside so leads to state change) -> state change -> render render (state change inside so leads to state change) ... infinite loop.
I have 3 components in nextjs and i want to achieve the below snippet in nextjs
<Route path="/" component={homePage} />
<Route path="/about" component={aboutPage} />
<Route path="/faq" component={faqPage} />
Q1. How can i do the same in nextjs without page refresh? (without react-router)
(Edit : some scholars are suggesting to read the docs but i have read it thoroughly and what i want is to pass a component along with the route)
Is this even possible in next js?
Q2: If i have url as /products?product_id=productid and on refresh if i want the url to be /products (basically i want to remove all params on refresh) What is the best practice to do this?
Thanks in advance
NextJS functions on a convention-based filesystem-based routing. You'd need to place your components in a directory structure that matches the routes you are wanting.
More details here:
https://nextjs.org/docs/routing/introduction
The Next.js docs don't really cover how to change away from <Route> components, however they have a lot of examples as code on how to do most things with Next.js. https://github.com/vercel/next.js/tree/canary/examples/layout-component
The below is what I used as an alternative to the component (there's no direct Next.js alternative).
_app.js
export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)
return getLayout(<Component {...pageProps} />)
}
Any page:
import Layout from '../components/layout'
import Sidebar from '../components/sidebar'
export default function About() {
return (
<section>
<h2>Layout Example (About)</h2>
<p>
This example adds a property <code>getLayout</code> to your page,
allowing you to return a React component for the layout. This allows you
to define the layout on a per-page basis. Since we're returning a
function, we can have complex nested layouts if desired.
</p>
<p>
When navigating between pages, we want to persist page state (input
values, scroll position, etc) for a Single-Page Application (SPA)
experience.
</p>
<p>
This layout pattern will allow for state persistence because the React
component tree is persisted between page transitions. To preserve state,
we need to prevent the React component tree from being discarded between
page transitions.
</p>
<h3>Try It Out</h3>
<p>
To visualize this, try tying in the search input in the{' '}
<code>Sidebar</code> and then changing routes. You'll notice the input
state is persisted.
</p>
</section>
)
}
About.getLayout = function getLayout(page) {
return (
<Layout>
<Sidebar />
{page}
</Layout>
)
}
The main part for the layout that you want to wrap around the pages, components/layout.js:
import Head from 'next/head'
import styles from './layout.module.css'
export default function Layout({ children }) {
return (
<>
<Head>
<title>Layouts Example</title>
</Head>
<main className={styles.main}>{children}</main>
</>
)
}
What's happening is the _app.js wraps all pages inside the declared layout. Each page then defines what layout that page belongs to. The layout then accepts a page as the {children} prop object of which you can then render anywhere in your layout page.
Next uses filesystem based routing, your folder structure should look like
-- pages
-- index.js
-- about/index.js
-- faq/index.js
For the custom component part, make a component that's clickable, on click, use next builtin router to redirect
const router = useRouter();
router.push('/');
I am currently creating a Next.js project and currently have a custom Layout implemented. I would like to reset my header component when a new page is clicked so that the menu will go back to its default version. Does anyone know how to do this?
import { Fragment } from "react";
import Header from "./header";
import Footer from "./footer";
import classes from "./layout.module.css";
function Layout(props) {
return (
<Fragment>
<Header />
<main className={classes.body}>{props.children}</main>
<Footer className={classes.bottom} />
</Fragment>
);
}
export default Layout;
Yes, you can easily do it using key, which is super cool.
function Layout(props) {
return (
<Fragment>
<Header key={props.selectedRoutePathName} />
<main className={classes.body}>{props.children}</main>
<Footer className={classes.bottom} />
</Fragment>
);
}
You can select a unique value (in your case, route pathname) and pass it as a key to the Header component.
And when the route path name changes, React will think of it as a different component and create another one. So the Header component will get the default state as you expected.
You can check my code-sandbox here for reference.
https://codesandbox.io/s/react-hooks-counter-demo-forked-t1llj?file=/src/index.js
Does React always load the components from top to bottom by default?
For example if I have this
class App extends Component {
render(){
return (
<div>
<h1>EXAMPLE</h1>
<First />
<Second />
<Third />
<Fourth />
</div>
)
}
}
Will the sequence of mounting by default be always
First, Second, Third, Fourth ....from top to bottom like shown above???
-Yes, React does use top to bottom approach.This approach is useful when react creates virtualDOM and updates it's own DOM.
-have look to this https://reactjs.org/docs/reconciliation.html
How can I specify that a component should be rendered absolutely before any other component?
I want to specify that <Footer /> and all the child components of footer should be rendered before any other components.
The reason I want this is because I have code that depends on the html that footer is rendering which means that the reference to <Footer /> is undefined in the other components if <Footer /> doesn't render first.
Here's an example:
export default class Layout extends React.Component {
...
render(){
return (
<Body />
<Footer /> //Render first
);
}
}
The only way I see for you do do that is:
Have the information about the render status for the footer in a state. (Let's assume your name it isFooterRendered and it is a boolean)
You set isFooterRendered to be false in the initial state.
You only render the children components when isFooterRendered is true
In componentDidMount you will have a reference to Footer, set isFooterRendered to be true.
(Some people claim that it's bad to setState on componentDidMount but in your case looks like a legit use case, aside from that React Docs expose a similar example)