NextJS recently released version 13.4, which stabilizes the App Router and changes it to the default and recommended solution in the official CLI. However, the App Router introduces the concept of React Server Components (referred to as RSC) and changes a considerable number of APIs, making it more difficult to learn.
Server Components#
In the App Router, NextJS distinguishes between Client Components and Server Components. Server Components are a special type of React component that runs on the server-side instead of the browser. Because they don't have state, they cannot use client-side features like useState and useEffect. They are typically used for data fetching or rendering components (for example, rendering Markdown, which has corresponding JavaScript dependencies only on the client-side), thereby reducing the client-side bundle size.
By default, all files in the App Router are server components. If you want to use client components, you need to add the use client
directive. However, this directive affects child components as well. This means that if the parent component has the use client
directive, all child components in that file will be treated as client components, even if they don't have the directive. Therefore, it is important to plan the layout properly and separate client components using Layout.
The <MyComponent />
below is actually a client component.
"use client";
import { useState } from "react";
import MyComponent from "./MyComponent";
export default function Home() {
const [num, setNum] = useState(0);
return (
<main>
<h1>{num}</h1>
<button onClick={() => setNum(num + 1)}>+1</button>
<MyComponent />
</main>
);
}
import { useEffect } from "react";
const MyComponent = () => {
useEffect(() => {
console.log("client component");
}, []);
return <div>123</div>;
};
export default MyComponent;
Furthermore, many third-party libraries currently do not support use client
, which can result in errors like the one below.
To use them in RSC, we need to make some special adjustments. Taking framer-motion as an example, it is usually wrapped around the outermost component.
"use client"
import { FC, PropsWithChildren } from "react";
import { motion } from "framer-motion";
const MotionLayout: FC<PropsWithChildren> = ({ children }) => {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
>
{children}
</motion.div>
);
};
export default MotionLayout;
Alternatively, you can encapsulate it as follows:
"use client"
import { motion } from "framer-motion";
export const MotionDiv = motion.div;
Data Fetching#
Fetching data is an important part of RSC. You can do it like this:
export default async function Home() {
const data = await fetch("https://api.github.com/repos/vercel/next.js").then(
(res) => res.json()
);
return <div>{data.id}</div>;
}
However, note that this is the default for SSG (Static Site Generation). NextJS has made some modifications to the native fetch. If you want to use SSR (Server-Side Rendering), you need to add the cache
configuration:
export default async function Home() {
const data = await fetch("https://api.github.com/repos/vercel/next.js", {
cache: "no-store",
}).then((res) => res.json());
return <div>{data.id}</div>;
}
The following is an example of ISR (Incremental Static Regeneration):
// ISR refreshes every 10 seconds
export default async function Home() {
const data = await fetch("https://api.github.com/repos/vercel/next.js", {
next: {
revalidate: 10,
},
}).then((res) => res.json());
return <div>{data.id}</div>;
}
If you are using axios or similar libraries and want to use SSR, you can do it like this:
import axios from "axios";
export const dynamic = "force-dynamic";
export default async function Home() {
const { data } = await axios.get(
"https://api.github.com/repos/vercel/next.js"
);
return <div>{data.id}</div>;
}
Routing#
The routing in the App Router has been enhanced based on the previous version. You can group routes using (folderName)
. The group name in parentheses does not affect the actual route. This improves code readability and allows for sharing layouts.
Dynamic routes are defined using [folderName]
, and now you can directly access the corresponding values in props.
You can also create a loading.tsx
file, which wraps the content in a Suspense component. This allows you to display the content in loading.tsx
while fetching data in page.tsx
.
There is also an error.tsx
file, which can be used as a fallback when rendering errors occur, preventing a blank screen with black text.
Another feature is Parallel Routes
, where you can map @folderName
to the props of the layout and use it as a "slot". This is useful for components like the header and footer.
export default function Layout(props: {
children: React.ReactNode;
analytics: React.ReactNode;
team: React.ReactNode;
}) {
return (
<>
{props.children}
{props.team}
{props.analytics}
</>
);
}
Conclusion#
In conclusion, the App Router has many APIs, and the ones mentioned here are just a small part. It is recommended to refer to the official documentation for more details.
This article is synchronized and updated to xLog by Mix Space.
The original link is https://suemor.com/posts/programming/approuter