352 lines
No EOL
7.9 KiB
Markdown
352 lines
No EOL
7.9 KiB
Markdown
Coding 3
|
|
|
|
**Link Component Navigation**
|
|
- To enable client-side navigation Next.js provides us with the Link component.
|
|
- The `<Link>` component is a React component that extends the HTML `<a>` element and is primary way for navigation
|
|
- To use it, we need to import it from 'next/link'.
|
|
Structure
|
|
```text
|
|
src/app
|
|
blog
|
|
page.jsx
|
|
products
|
|
page.jsx
|
|
page.jsx
|
|
```
|
|
In `src/app/page.jsx`
|
|
```jsx
|
|
import Link from "next/link";
|
|
|
|
const Home = () => {
|
|
return (
|
|
<>
|
|
<h1> Hello </h1>
|
|
<Link href="/blog">Blog</Link>
|
|
<Link href="/products">Products</Link>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Home;
|
|
|
|
```
|
|
In `src/app/blog/page.jsx` `/` path is used to go to root page
|
|
```jsx
|
|
import Link from "next/link";
|
|
|
|
export default function Blog() {
|
|
return (
|
|
<>
|
|
<h1>Blog</h1>
|
|
<Link href="/">Home</Link>
|
|
</>
|
|
);
|
|
}
|
|
|
|
```
|
|
**For Dynamic Routes**
|
|
Structure
|
|
```text
|
|
src/app
|
|
blog
|
|
page.jsx
|
|
products
|
|
[productId]
|
|
page.jsx
|
|
page.jsx
|
|
page.jsx
|
|
```
|
|
In `src/app/products/page.jsx`
|
|
```jsx
|
|
import Link from "next/link";
|
|
|
|
export default function ProductList() {
|
|
const productId = 100;
|
|
return (
|
|
<>
|
|
<h1>Product List</h1>
|
|
<h2>Product 1</h2>
|
|
<h2>Product 2</h2>
|
|
<Link href={"products/3"} replace>
|
|
{" "}
|
|
<h2>Product 3</h2>{" "}
|
|
</Link>
|
|
<Link href={`products/${productId}`}>
|
|
{" "}
|
|
<h2>Product {productId}</h2>{" "}
|
|
</Link>
|
|
</>
|
|
);
|
|
}
|
|
|
|
```
|
|
- Link for Dynmaic routes is generated just like we access the value of variable.
|
|
With `replace` keyword we can clear the url stack history means after navigating to `localhost:3000/products/3` and when we click back button we will navigate to `localhost:3000` not `localhost:3000/products`.
|
|
|
|
|
|
**Active Links**
|
|
- First we need to create links for login, register, forgot-password in layout.jsx page for `(auth)`. So instead of creating these 3 links for each page ie login, register and forgot-password we used layout.jsx to serve this as template/same for all route groups.
|
|
Structure
|
|
```text
|
|
src/app
|
|
(auth)
|
|
login
|
|
password
|
|
forgot-password
|
|
layout.jsx
|
|
page.jsx
|
|
```
|
|
In `src/app/(auth)/layout.jsx`
|
|
```jsx
|
|
import Link from "next/link";
|
|
|
|
const navLinks = [
|
|
{ name: "Register", href: "/register" },
|
|
{ name: "Login", href: "/login" },
|
|
{ name: "Forgot Password", href: "/forgot-password" },
|
|
];
|
|
|
|
export default function OuterAuthLayout({ children }) {
|
|
return (
|
|
<div className="">
|
|
{navLinks.map((link) => {
|
|
return <Link href={link.href}>{link.name}</Link>;
|
|
})}
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
```
|
|
- So in order to style it we need to know the path of current url so we need to use the `usePathname` from 'next/navigation' hook. As we r using hook we need client side rendering for this and have to type `use client` at top in jsx file.
|
|
Structure
|
|
```text
|
|
src/app
|
|
(auth)
|
|
login
|
|
password
|
|
forgot-password
|
|
layout.jsx
|
|
style.css
|
|
page.jsx
|
|
```
|
|
In `src/app/(auth)/layout.jsx`. Use the particular class for active link
|
|
```jsx
|
|
"use client";
|
|
import Link from "next/link";
|
|
import { usePathname } from "next/navigation";
|
|
|
|
import "./style.css";
|
|
|
|
const navLinks = [
|
|
{ name: "Register", href: "/register" },
|
|
{ name: "Login", href: "/login" },
|
|
{ name: "Forgot Password", href: "/forgot-password" },
|
|
];
|
|
|
|
export default function OuterAuthLayout({ children }) {
|
|
const pathname = usePathname();
|
|
return (
|
|
<div className="">
|
|
{navLinks.map((link) => {
|
|
const isActive = pathname.startsWith(link.href);
|
|
return (
|
|
<Link
|
|
href={link.href}
|
|
key={link.name}
|
|
className={isActive ? "active-link" : ""}
|
|
>
|
|
{link.name}
|
|
</Link>
|
|
);
|
|
})}
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
```
|
|
In `src/app/(auth)/style.css`
|
|
```css
|
|
a {
|
|
margin: 0 1rem;
|
|
color: blue;
|
|
}
|
|
|
|
a.active-link {
|
|
color: black;
|
|
}
|
|
|
|
```
|
|
|
|
|
|
**Navigating Programitically**
|
|
- Instead of using jsx, javascript can be used too so for that we can use the hook ie `useRouter` from 'next/navigation'.
|
|
Structure
|
|
```text
|
|
src/app
|
|
order-product
|
|
page.jsx
|
|
page.jsx
|
|
```
|
|
In `src/app/order-product/page.jsx`
|
|
```jsx
|
|
"use client";
|
|
import { useRouter } from "next/navigation";
|
|
|
|
export default function OrderProduct() {
|
|
const router = useRouter();
|
|
const handleClick = () => {
|
|
console.log("order placed");
|
|
router.push("/");
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<h1>Order Product</h1>
|
|
<button onClick={handleClick}>Place Order</button>
|
|
</>
|
|
);
|
|
}
|
|
|
|
```
|
|
router.push('/') will push the root route to url stack and when we click on back after going through homepage it will brink back to the page `localhost:3000/order-product`. In `Link` coponent there was a attribute named replace which clears the url stack so to do same programitically use `router.replace('/')`. Not only that but we can go to previous or next url stack too by `router.back()` and `router.forward()`
|
|
|
|
|
|
**Templates**
|
|
- Templates are similar to layouts in the way they wrap each child layout or page
|
|
But with templates when a user navigates between routes that share a template, a new instance of component is mounted, DOM elements are recreated, state is not preserved.
|
|
- So to only to be used once we dont have to conserve states and need a template to recreate pages so their states dont conflict with each other
|
|
Without template
|
|
```text
|
|
src/app
|
|
(auth)
|
|
login
|
|
password
|
|
forgot-password
|
|
layout.jsx
|
|
style.css
|
|
page.jsx
|
|
```
|
|
In `src/app/(auth)/layout.jsx`
|
|
```jsx
|
|
"use client";
|
|
import Link from "next/link";
|
|
import { usePathname } from "next/navigation";
|
|
|
|
import "./style.css";
|
|
|
|
import { useState } from "react";
|
|
|
|
const navLinks = [
|
|
{ name: "Register", href: "/register" },
|
|
{ name: "Login", href: "/login" },
|
|
{ name: "Forgot Password", href: "/forgot-password" },
|
|
];
|
|
|
|
export default function OuterAuthLayout({ children }) {
|
|
const pathname = usePathname();
|
|
const [input, setInput] = useState("");
|
|
|
|
return (
|
|
<div className="">
|
|
<div className="">
|
|
<input
|
|
type="text"
|
|
value={input}
|
|
onChange={(e) => setInput(e.target.value)}
|
|
/>
|
|
</div>
|
|
{navLinks.map((link) => {
|
|
const isActive = pathname.startsWith(link.href);
|
|
return (
|
|
<Link
|
|
href={link.href}
|
|
key={link.name}
|
|
className={isActive ? "active-link" : ""}
|
|
>
|
|
{link.name}
|
|
</Link>
|
|
);
|
|
})}
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
```
|
|
When we move to new route input will be same and wont be reset.
|
|
**With templates**
|
|
Just change the file name from layout.jsx to template.jsx and now input will be reset and nothing will be preserved.
|
|
Some limitations though. If there is a route group(say named inner-auth) like this
|
|
Structure
|
|
```text
|
|
src/app
|
|
(auth)
|
|
(inner-auth)
|
|
login
|
|
password
|
|
forgot-password
|
|
template.jsx
|
|
style.css
|
|
page.jsx
|
|
```
|
|
In this case, states between `login` and `password` routes will be shared not with `forget-password` route though.
|
|
|
|
|
|
**Loading UI**
|
|
- Till now different files like `page.jsx`, `layout.jsx`, `not-found.jsx` and `template.jsx` have been used. There is another one ie `loading.jsx`.
|
|
Structure
|
|
```text
|
|
src/app
|
|
blog
|
|
first
|
|
second
|
|
loading.jsx
|
|
layout.jsx
|
|
page.jsx
|
|
page.jsx
|
|
```
|
|
In `src/app/blog/loading.jsx`
|
|
```jsx
|
|
export default function LoadingBlog() {
|
|
return (
|
|
<div className="LoadingBlog">
|
|
<h1>Loading</h1>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
```
|
|
To test it, make sure that `src/app/blog/page.jsx` loads slower due to setTimeout. Make sure that layout.jsx wont load slow as first layout.jsx is loaded then loading.jsx contents then page.jsx.
|
|
|
|
In `src/app/blog/page.jsx`
|
|
```jsx
|
|
import Link from "next/link";
|
|
|
|
export const metadata = {
|
|
title: {
|
|
absolute: "blog",
|
|
default: "blog",
|
|
},
|
|
};
|
|
|
|
function promiseSetTimeOut(time) {
|
|
return new Promise((resolve, reject) => {
|
|
setTimeout(() => {
|
|
resolve();
|
|
}, time);
|
|
});
|
|
}
|
|
|
|
export default async function Blog() {
|
|
await promiseSetTimeOut(1000);
|
|
return (
|
|
<>
|
|
<h1>Blog</h1>
|
|
<Link href="/">Home</Link>
|
|
</>
|
|
);
|
|
}
|
|
|
|
``` |