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)
Related
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'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
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
I have set up a basic react app with hash navigation. Problem is when I click on any link to navigate between pages, I see that the hash in the url is changing properly, as well as I added a console.log in my layour's render to see if it's getting called and it is, with proper this.props.children values, however the page is not rendering anything. If I go to any route and refresh the page I see the correct components rendered, but if I navigate somewhere from there noting gets rendered until I refresh again.
import React from "react";
import ReactDOM from "react-dom";
import { IndexRoute, Router, Route, Link, hashHistory as history } from 'react-router';
class Layout extends React.Component {
render() {
console.log(this.props, document.location.hash);
return <div>
<div>
<span>LEYAUTI MLEAYTI {Math.random()}</span>
</div>
<div>
{this.props.children}
</div>
<div>
{this.props.params.project}
</div>
</div>
}
}
class CreateProject extends React.Component {
render() {
return <div>
<h1>Create PROEKT</h1>
</div>
}
}
class Projects extends React.Component {
render() {
return <div>
<h1>PROEKTI MROEKTI</h1>
<Link to="/projects/create">New project</Link>
</div>
}
}
ReactDOM.render(
<Router history={history}>
<Route path="/" component={Layout}>
<IndexRoute component={Projects}/>
<Route path="projects/create" component={CreateProject}/>
</Route>
</Router>,
document.getElementById('app-root'));
Here is a visual of what's happening in the console when I navigate on a couple routes, but the DOM remains unchanged
This may be an issue with hashHistory. Which react-router version are you using? With v4 and above, you need to use history like so -
import createHistory from 'history/createHashHistory'
const history = createHistory()
// pass history to the Router....
Your component didn't actually unmount/remount if you only update your hashtag in your url. The route however, is updated. So you can only see the component loads content for once when you refresh the page.
You will need to create state variables and update it in a routeChange handler callback and bind the updated state variable to your view by using setState. Then the component can get updated.
See this post for how to add the route change listener (https://github.com/ReactTraining/react-router/issues/3554)
Alright, so I got down to the bottom of it.
The problem was that I was including my client.min.js file before the default app.js file of laravel 5.4's default layout. For some reason it broke react in a very weird way. What I had to do is switch the order in which the two files were included.
I made a test where I do React.render() in a child component and pass a prop to it. The app structure looks like:
<App>
<Child />
</App>
Then in <Child />, I render <Outside /> component with another React.render(). When I checked in Chrome React firebug, the App structure is:
<App>
<Child />
</App>
<Outside />
instead of:
<App>
<Child />
<Outside />
</App>
However, when passing a {selected : true } state from <App />, it displays well as a prop in <Outside />, but when I make a state change to { selected : false } in <App />, both <Child /> and <Outside /> don't receive the updated prop. I think it happens because <Outside /> is out of the <App /> scope so the data flow doesn't flow well. (Sorry for my English)
You can check the test here: http://jsbin.com/yazaqo/1/edit?js,console,output
What I'm trying to ask is: Is there any other way to update a component which is outside the App scope?
The Child component is receiving the updated prop, its just not being logged to the console.
Only render and componentWillReceiveProps are invoked when a component receives new props, so if you move the console.log to either one of those methods, you'll see the updated value.
This is also why Outside is not receiving the updated prop. You're rendering it in componentDidMount, which is only invoked once, when the component is mounted.
It should be rendered in Child's render method the same way Child is rendered in the App component. The other option would be to render it in componentWillReceiveProps, although you may run into some problems doing it that way.
Hope this helps.