Dynamic routing results in 404 - next.js

Following this guide, I created the following file in my project:
/pages/user/[id].js
class Post extends Component {
render() {
return (
<React.Fragment>
<Navbar />
<Content />
<Footer />
</React.Fragment>
);
}
}
export default Post;
But when I go to that URL, I get a 404.
What is the problem?

Assuming you're visiting (for example), http://localhost:3000/user/something (where something is your id), try also visiting http://localhost:3000/user/something/ (note the backslash). This is currently a known issue in Next with dynamic routing.
(This also assumes you don't have pages/user/something.js in your project as dynamic routes take a back seat to explicitly named routes.)

Related

404 page Next.js 13

I`m currently learning Next.js and stumbled upon a problem. When I have this folder configuration https://i.stack.imgur.com/lSedV.png and code below everything works, but making folders look like this https://i.stack.imgur.com/WfLvm.png and clicking link gives me 404 page. Is there any way to make code work with the second variant?
function Event({ id, title, image, category }) {
return (
<>
<div>{title}</div>
<Link href={`/${category}/${id}`}>
<Image src={image} width={800} height={800} />
</Link>
</>
);
}
export default Event;

Conditional Rendering the components, Next.js style broken on first load

I am using Ant Design and Custom Stylesheet. On first load style is broken, but when I visit another page and come back to broken page, now it looks fine. So the problem is only on the first load. It's on the development server, I have been clear all cache. But still the same issue.
Here is the screenshot how it's looking like after first load
Here is the correct style after I come back from another page
Here is the code how I am rendering the components:
<div>
{jwToken || role === "restaurant_owner" ? (
<Layout>
<Index />
</Layout>
) : (
<div>
<Login />
</div>
)}
</div>
I had a simliar issue, the way I fixed it was to add a mounted variable that depended on the condition. So it looks this.
// Not sure how you pass the condition, I'm assuming hooks
const { condition } = someHook()
const [mounted, setMounted] = useState()
useEffect(() => {
setMounted(true)
return () => setMounted(false)
}, [condition]);
return (
<div>
{mounted && condition && <Component/>
</div>
)
As to why this happens, I suspect it has to do with SSR (I found simliar issue on Github but for Material-UI) and my solution forces the condition to be available only during the browser.

How to make components consist between pages?

When you go to about page from index page, RestOfTheApp rerenders. Maybe it should be this way with SSR, but nextjs added static rendering like gatsby. Isn't there a way to prevent components rerendering? Like: a header shouldn't change between pages.
about.js
function index() {
return (
<>
<RestOfTheApp>
about
</RestOfTheApp>
</>
)
}
index.js
function index() {
return (
<>
<RestOfTheApp>
index
</RestOfTheApp>
</>
)
}
You can keep component state with redux I assume, but whole page re-rendering when you just need to fetch some blog content seems bloaty. I've tested with some basic layout, it still seems lightning fast but not re-rendering whole page is the main concept of SPA's, I am a little heart broken 💔
Every component that you include in a page (under /pages) will re-render no matter what you do. But it's definitely possible to add a persistent layout which doesn't re-render in NextJs. The solution is the custom app component. You can read more about it here
Following example can be helpful to understand how you can create a persistent layout:
// /pages/_app.js
import React from 'react';
import App from 'next/app';
import Layout from '../components/Layout';
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
return (
<Layout>
<Component {...pageProps}></Component>
</Layout>
)
}
}
export default MyApp
In this case, the Layout component will not re-render when you navigate between pages.
#Ankit has already given a great answer on HOW to create a persistent component. Here is WHY it works.
So what really happens when you navigate from some page A (defined in pages/a.js) to another page B (defined in pages/b.js)? First, the navigation takes place on the client-side. This means instead of fetching the rendered HTML from the server, some JavaScript is run in the browser to render the new page. (You can verify it here.) The JavaScript logic of page navigation boils down to this:
The JavaScript code of the new page component <B /> is fetched from the server, if it's not already prefetched;
Next.js will call ReactDOM.render() with 2 arguments: the first one is the new React element to render (it can be roughly thought of as the updated App component), and the second one is the DOM container element (it's always <div id="__next"></div>) that the new React element is rendered into.
In short, this process can be roughly thought of as rendering the updated App component into the <div id="__next"></div> DOM container element. React will then take care of diffing the new and old React elements and decide which part of the DOM to re-render and which part to update.
So what does the new and old React element look like? Well, the default definition of the App component looks like this:
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
// Component will be set to the current page component
return <Component {...pageProps} />
}
export default MyApp
Where the Component variable will be set to the current page component. This means the old React element will look like this:
<A {...pageProps} />
and the new React element will look like this:
<B {...pageProps} />
According to the React diffing algorithm, when comparing the new and old React element, if the two elements being compared are of different types, then the corresponding subtree will be entirely destroyed and re-rendered.
That's exactly what happens in this case. <A /> and <B /> are two different components and are considered as of different types, so the part of the DOM that corresponds to <A /> will be destroyed and re-rendered as <B />.
That's why the entire page component will be re-rendered when you navigate to a new page, even if they include common components like the header.
If you put the header in the custom App components, like this:
import Header from '../components/header'
function MyApp({ Component, pageProps }) {
return (
<div>
<Header />
<Component {...pageProps} />
</div>
)
}
export default MyApp
Then the <Header /> component will persist across page navigations, because the React diffing algorithm will consider it as the same type and will only make minimal updates to the subtree.

How to programmatically change React Router page status?

So, I have a relatively complex setup involving react-router and react-async-connect to render stuff server-side.
Here's my route setup:
<Route path='/' component={App}>
<Route path='profile/:userID' component={UserProfile} />
<Route path='*' component={NotFound} status={404} />
</Route>
So, when someone hits a profile/{userID} path, obviously the UserProfile component is rendered, which in turn makes a call to an external API to get props. However, if this API returns a 404, I want to display my 404 page.
The trouble is that it's a 404 from an external service, not a "real" router 404. How does one trigger this?
I really want to avoid importing the NotFound component into UserProfile component and doing it that way, seems quite dirty.
You can achieve this by using browserHistory function:
// UserProfile.js
import { browserHistory } from 'react-router';
// In place where you need redirect to 404:
browserHistory.push('/404-page')
I think, you can redirect to any route that don't matches your defined routes, as you are redirecting to NotFound page at every not matched route.
Keep your 404 application status in store/reducer as default false.
Upon getting 404 status code - set it to true.
Once user is clicked other link - reset to false.
In your App.render() do smth like this:
render() {
return isNotFound ? <NotFound /> : {this.props.children};
}
I've just found this library, react-server-status, that provides a friendly and easier way to provide the status page.
import React, { Component } from 'react';
import ServerStatus from 'react-server-status';
export default class NotFound extends Component {
render() {
return (
<ServerStatus status={ 404 }>
<div>Some content</div>
</ServerStatus>
);
}
}

React Router - one of two routes

I know the title may be vague, so I'll try to describe my problem as good as I can.
I am doing a WordPress theme and I need to link to both categories and tags pages. Their structure is /[categories|tags]/item. Now I need to make a route for those two items only. I tried doing it like this:
<Route path="/category/:slug" component={Archives} />
<Route path="/tag/:slug" component={Archives} />
But with this I have no distinction of wether this is a category or a tag. I dont wan't to go with <Route path="/:type/:slug" component={Archives} /> because this may get confusing when I'll need to use nested pages. I also don't want to change the url scheme.
You can use queries to determine what the type is:
<Link to={{ pathname: '/category/[slug]', query: { type: 'category' } }} activeClassName="active">Some Link</Link>
Then access the queries in your Archives view via:
this.props.location.query // you could set up some conditional logic based on type
UPDATE: Without changing the url scheme
...
<Route path="/category/:slug" component={Archives} />
<Route path="/tag/:slug" component={Archives} />
// Then in Archives component you could do
class Archive extends React.Component {
...
render() {
const path = this.props.location.pathname;
const category = path.indexOf('/category/') !== -1;
const tag = path.indexOf('/tag/') !== -1;
... // some logic based on tag and category
return {
...
}
}
}

Resources