Nextjs Docs 요약

Jake_Young·2023년 8월 7일
0

Nextjs

✅ Official Docs Summary

  • 20230805

App router

  • Basically all pages are server side rendered components
  • If some components are not the SSR, then you should notice it with use client
  • If you want to use window or state, then that component should be treated as a client component


Unsupported Pattern

  • Importing Server Components into Client Components
"use client";

// This pattern will **not** work!
// You cannot import a Server Component into a Client Component.
import ExampleServerComponent from "./example-server-component";

export default function ExampleClientComponent({
  children,
}: {
  children: React.ReactNode;
}) {
  const [count, setCount] = useState(0);

  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      <ExampleServerComponent />
    </>
  );
}

Recommended Pattern

  • Passing Server Components to Client Components as Props
"use client";

import { useState } from "react";

export default function ExampleClientComponent({
  children,
}: {
  children: React.ReactNode;
}) {
  const [count, setCount] = useState(0);

  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>

      {children}
    </>
  );
}

Tips

  • The very same strategy of "lifting content up" has been used to avoid state changes in a parent component re-rendering an imported nested child component.

  • Props passed from the Server to Client Components need to be serializable. This means that values such as functions, Dates, etc, cannot be passed directly to Client Components.


Keeping Server-Only Code out of Client Components (Poisoning) 1

export async function getData() {
  const res = await fetch("https://external-service.com/data", {
    headers: {
      authorization: process.env.API_KEY,
    },
  });

  return res.json();
}

Keeping Server-Only Code out of Client Components (Poisoning) 2

  • At first glance, it appears that getData works on both the server and the client. But because the environment variable API_KEY is not prefixed with NEXT_PUBLIC, it's a private variable that can only be accessed on the server. Next.js replaces private environment variables with the empty string in client code to prevent leaking secure information.

The "server only" package 1

  • To prevent this sort of unintended client usage of server code, we can use the server-only package to give other developers a build-time error if they ever accidentally import one of these modules into a Client Component.

  • The corresponding package client-only can be used to mark modules that contain client-only code – for example, code that accesses the window object.


The "server only" package 2

import "server-only";

export async function getData() {
  const res = await fetch("https://external-service.com/data", {
    headers: {
      authorization: process.env.API_KEY,
    },
  });

  return res.json();
}

Third-party packages 1

  • Today, many components from npm packages that use client-only features do not yet have the directive. These third-party components will work as expected within your own Client Components since they have the "use client" directive, but they won't work within Server Components.

Third-party packages 2

import { Carousel } from "acme-carousel";

export default function Page() {
  return (
    <div>
      <p>View pictures</p>

      {/* Error: `useState` can not be used within Server Components */}
      <Carousel />
    </div>
  );
}
"use client";

import { Carousel } from "acme-carousel";

export default Carousel;

How to set context at the root components 1

"use client";

import { createContext } from "react";

export const ThemeContext = createContext({});

export default function ThemeProvider({ children }) {
  return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>;
}

How to set context at the root components 2

import ThemeProvider from "./theme-provider";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}

Parellel Routes / Intercepting Routes 🔥


Layouts

  • A layout is UI that is shared between multiple pages. On navigation, layouts preserve state, remain interactive, and do not re-render. Layouts can also be nested.

  • You can define a layout by default exporting a React component from a layout.js file. The component should accept a children prop that will be populated with a child layout (if it exists) or a child page during rendering.

  • Layouts in a route are nested by default. Each parent layout wraps child layouts below it using the React children prop.


Automatic fetch() Request Deduping

  • Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will automatically dedupe the requestswithout affecting performance.

  • Layouts do not have access to the current route segment(s). To access route segments, you can use useSelectedLayoutSegment or useSelectedLayoutSegments in a Client Component.


Template VS Layout

  • 페이지 가서 자세히 읽어보자ㅋㅋ
  • 아무튼 Template은 추천 안한다.
  • 특별한 이유 아니면 Layout만 써라.
  • Template은 Rerendering 마다 새로 instance를 만들어서 상태 유지가 안 된다.
  • 그래서 페이지 진입 시, 매번 새로 애니메이션을 보여줘야하는 것 같은 특별한 경우 아니면 쓸 일 없다.

Modifying 'head'

  • In the app directory, you can modify the 'head' HTML elements such as title and meta using the built-in SEO support.

  • Good to know: You should not manually add 'head' tags such as 'title' and 'meta' to root layouts. Instead, you should use the Metadata API which automatically handles advanced requirements such as streaming and de-duplicating 'head' elements.


Linking and Navigating

  • There are two ways to navigate between routes:

    • 'Link' Component
    • useRouter Hook
  • If you'd like to scroll to a specific id on navigation, you can append your URL with a # hash link or just pass a hash link to the href prop. This is possible since 'Link' renders to an 'a' element.

  • 'Link' is a React component that extends the HTML 'a' element to provide prefetching and client-side navigation between routes. It is the primary way to navigate between routes in Next.js.

  • Recommendation: Use the 'Link' component to navigate between routes unless you have a specific requirement for using useRouter.


Soft Navigation

  • On navigation, Next.js will use soft navigation if the route you are navigating to has been prefetched, and either doesn't include dynamic segments or has the same dynamic parameters as the current route.

  • Hard Navigation이란 용어도 있다.

  • 한 마디로 모두 새로 다시 그리는 페이지 이동을 뜻한다..


Route Groups 1


Route Groups 2

  • A route group can be created by wrapping a folder's name in parenthesis: (folderName)
  • 한 마디로 기능은 없다.
  • App router에서 쓸 수 있는 convention의 하나.
  • 이렇게 하면 route에 적용은 안되고 코드를 colocation 할 수 있다.

Dynamic Routes

  • A Dynamic Segment can be created by wrapping a folder's name in square brackets: [folderName]. For example, [id] or [slug].

  • Dynamic Segments are passed as the params prop to layout, page, route, and generateMetadata functions.


Catch-all Segments

  • Dynamic Segments can be extended to catch-all subsequent segments by adding an ellipsis inside the brackets [...folderName].

  • For example, app/shop/[...slug]/page.js will match /shop/clothes, but also /shop/clothes/tops, /shop/clothes/tops/t-shirts, and so on.


Optional Catch-all Segments

  • Catch-all Segments can be made optional by including the parameter in double square brackets: [[...folderName]].

  • For example, app/shop/[[...slug]]/page.js will also match /shop, in addition to /shop/clothes, /shop/clothes/tops, /shop/clothes/tops/t-shirts.


Error Handling

  • An error.js boundary will not handle errors thrown in a layout.js component in the same segment because the error boundary is nested inside that layouts component.

  • To specifically handle errors in these root components, use a variation of error.js called app/global-error.js located in the root app directory. Because of this, it is important to note that global-error.js must define its own <html> and <body> tags.


Route Handlers 1

  • Route Handlers are only available inside the app directory. They are the equivalent of API Routes inside the pages directory meaning you do not need to use API Routes and Route Handlers together.

  • But there cannot be a route.js file at the same route segment level as page.js.

  • 걍 바로 API도 App 폴더 안에 같이 만들 수 있다.


Route Handlers 2


Middleware 1

  • Middleware runs before cached content and routes are matched.

  • Use the file middleware.ts (or .js) in the root of your project to define Middleware. For example, at the same level as pages or app, or inside src if applicable.

  • 다음 두 페이지에 있는 우선 순위만 한번 봐두자


Middleware 2


Middleware 3


Project Organization and File Colocation

  • And, even when a route is made publicly accessible, only the content returned by page.js or route.js is sent to the client.

  • This means that project files can be safely colocated inside route segments in the app directory without accidentally being routable.

    • This is different from the pages directory, where any file in pages is considered a route.
    • While you can colocate your project files in app you don't have to. If you prefer, you can keep them outside the app directory.
  • Private folders can be created by prefixing a folder with an underscore: _folderName

  • You can create URL segments that start with an underscore by prefixing the folder name with %5F (the URL-encoded form of an underscore): %5FfolderName.


Edge and Nodejs Runtimes 🔥

  • Next의 Runtime에는 세 가지가 있다. 제대로 이해하게 되면 내게도 알려주자.

  • 기본값은 Nodejs이나 무거우니 가능하다면 Edge로 바꿔도 된다.


Fetching 1

  • use is a new React function that accepts a promise conceptually similar to await.

  • Wrapping fetch in use is currently not recommended in Client Components and may trigger multiple re-renders. For now, if you need to fetch data in a Client Component, we recommend using a third-party library such as SWR or React Query.

  • Caching at the fetch level with revalidate or cache: 'force-cache' stores the data across requests in a shared cache. You should avoid using it for user-specific data (i.e. requests that derive data from cookies() or headers())


Fetching 2

import { getArtist, getArtistAlbums, type Album } from "./api";

export default async function Page({
  params: { username },
}: {
  params: { username: string };
}) {
  // Initiate both requests in parallel
  const artistData = getArtist(username);
  const albumData = getArtistAlbums(username);

  // Wait for the artist's promise to resolve first
  const artist = await artistData;

  return (
    <>
      <h1>{artist.name}</h1>
      {/* Send the artist information first,
          and wrap albums in a suspense boundary */}
      <Suspense fallback={<div>Loading...</div>}>
        <Albums promise={albumData} />
      </Suspense>
    </>
  );
}

// Albums Component
async function Albums({ promise }: { promise: Promise<Album[]> }) {
  // Wait for the albums promise to resolve
  const albums = await promise;

  return (
    <ul>
      {albums.map((album) => (
        <li key={album.id}>{album.name}</li>
      ))}
    </ul>
  );
}

Fetching 3

import prisma from "./lib/prisma";

export const revalidate = 3600; // revalidate every hour

async function getPosts() {
  const posts = await prisma.post.findMany();
  return posts;
}

export default async function Page() {
  const posts = await getPosts();
  // ...
}

Caching Data

  • Requests are not cached if:

    • Dynamic methods (next/headers, export const POST, or similar) are used and the fetch is a POST request (or uses Authorization or cookie headers)
    • fetchCache is configured to skip cache by default
    • revalidate: 0 or cache: 'no-store' is configured on individual fetch

React cache() 1

  • React allows you to cache() and deduplicate requests, memoizing the result of the wrapped function call. The same function called with the same arguments will reuse a cached value instead of re-running the function.

  • The cache arguments must be flat and only include primitives. Deep objects won't match for deduplication.

  • In this new model, we recommend fetching data directly in the component that needs it, even if you're requesting the same data in multiple components, rather than passing the data between components as props.


React cache() 2

import { cache } from "react";

export const getUser = cache(async (id: string) => {
  const user = await db.user.findUnique({ id });
  return user;
});

//

import { getUser } from "@utils/getUser";

export default async function UserLayout({
  params: { id },
}: {
  params: { id: string };
}) {
  const user = await getUser(id);
  // ...
}

Preload pattern with cache()

import { getUser } from "@utils/getUser";

export const preload = (id: string) => {
  // void evaluates the given expression and returns undefined
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
  void getUser(id);
};
export default async function User({ id }: { id: string }) {
  const result = await getUser(id);
  // ...
}

//

import User, { preload } from "@components/User";

export default async function Page({
  params: { id },
}: {
  params: { id: string };
}) {
  preload(id); // starting loading the user data now
  const condition = await fetchCondition();
  return condition ? <User id={id} /> : null;
}

Combining cache, preload, and server-only

  • With this approach, you can eagerly fetch data, cache responses, and guarantee that this data fetching only happens on the server.
import { cache } from "react";
import "server-only";

export const preload = (id: string) => {
  void getUser(id);
};

export const getUser = cache(async (id: string) => {
  // ...
});

Segment-level Caching

  • 컴포넌트 단위로 rebalidate 하려면 해당 컴포넌트 파일에서 예약어인 revalidate를 추가하면 된다.

  • export const revalidate = 60; // revalidate this segment every 60 seconds

Revalidating Data

  • There are two types of revalidation in Next.js:

    • Background: Revalidates the data at a specific time interval.

    • fetch("https://...", { next: { revalidate: 60 } });
      
      //
      
      export const revalidate = 60; // revalidate this page every 60 seconds
    • On-demand: Revalidates the data based on an event such as an update.


Server Actions

  • Server Functions called as an action.

  • Server Actions can be progressively enhanced by passing them to a form element's action prop. The form is interactive before any client-side JavaScript has loaded. This means React hydration is not required for the form to submit.

profile
자바스크립트와 파이썬 그리고 컴퓨터와 네트워크

0개의 댓글