카나리아 테스트
use 훅은 현재 React의 Canary과 실험실 채널에서만 사용할 수 있어요. 리액트의 릴리즈된 채널에 대해서 알아보려면 여기를 눌러주세요.
use
는 Promise
나 context
같은 리소스 값을 읽게 도와주는 리액트 훅이에요.
const value = use(resource);
Reference | 레퍼런스
use(resource)
Promise
나 context
같이 리소스 값을 읽어오려면 컴포넌트에서 use
를 호출하세요.
import { use } from 'react';
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
const theme = use(ThemeContext);
다른 리액트 훅과 달리 use
는 반복문 또는 if
문과 같은 조건문에서도 호출할 수 있어요. 다른 리액트 훅과는 달리 use
훅을 호출하는 함수는 반드시 컴포넌트거나 훅이어야만 해요.
Promise
와 함께 호출 될 때, use
훅은 Suspense
및 Error Boundary와 통합돼요. Promise
가 use
를 처리하는 동안 use
를 호출하는 컴포넌트는 대기해요. 만약 use
를 호출하는 컴포넌트가 Suspense boundary로 감싸져 있다면 fallback이 표시돼요. Promise
가 해결되면 Suspense fallback은 use
훅에서 리턴 받은 데이터를 사용하여 렌더링된 컴포넌트로 바뀌어요. 만약 Promise
가 거부되면 가장 가까운 Error Boundary의 fallback를 보여줘요.
더 많은 예시는 아래에서 확인하세요.
Parameters | 파라미터(매개변수)
resource
: 값을 읽어오고 싶은 데이터의 출처. resource
는 Promise
또는 context
가 될 수 있어요.
Returns | 반환값
use
훅은 Promise
또는 context
에서 해결된 값과 같이 출처에서 읽어온 값을 반환해요.
Caveat | 주의사항
use
훅은 반드시 컴포넌트 또는 훅 안에서만 호출되어야 해요.
서버 컴포넌트에서 데이터를 페치할 때는 use
보다는 async
와 await
를 권장해요. async
와 await
는 await
가 호출된 시점부터 렌더링을 시작하지만 use
는 데이터를 해결한 후에 컴포넌트를 리렌더해요.
클라이언트 컴포넌트에서 프로미스를 생성하는 것보다는 서버 컴포넌트에서 Promise를 생성하고 생성된 Promise를 클라이언트 컴포넌트로 전달하는 것을 추천해요. 클라이언트 컴포넌트에서 생성된 프로미스는 렌더링이 될 때마다 재생성돼요. 서버 컴포넌트에서 클라이언트 컴포넌트로 전달된 프로미스는 리렌더링 과정에서 안정적이에요. 아래 용법의 Streaming data from the server to the client | 서버에서 클라이언트로 데이터 스트림하기를 참고하세요.
Usage | 용법
Reading context with use
| use
로 context 읽어오기
use
훅으로 context가 전달될 때는 useContext
와 유사하게 동작해요. useContext
는 최상위 컴포넌트에서만 호출될 수 있는 반면 use
는 if
문과 같은 조건문이나 for
문과 같은 반복문에서도 호출될 수 있어요. use
훅은 useContext
보다 더 유연하기 때문에 더 선호되고 있어요.
import { use } from 'react';
function Button() {
const theme = use(ThemeContext);
// ...
}
use
훅은 인자로 전달된 context의 context 값을 반환해요. context 값을 정하기 위해 리액트는 컴포넌트 트리를 탐색하고 특정 context 상위의 가장 가까운 context provider를 찾아내요.
Button
으로 context를 전달하기 위해서는 Button
이나 부모 컴포넌트를 해당 context provider로 감싸세요(래핑하세요).
function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
// ... renders buttons inside ...
}
provider과 Button
컴포넌트 사이에 몇개의 층이 존재하든 상관 없어요. Form
컴포넌트의 어느 위치에 있는 Button
컴포넌트가 use(ThemeContext)
를 호출하면 해당 Context는 "dark"
라는 값을 전달받아요.
useContext
와는 달리 use
훅은 if
문 같은 조건문 안에서도 호출될 수 있어요.
function HorizontalRule({ show }) {
if (show) {
const theme = use(ThemeContext);
return <hr className={theme} />;
}
return false;
}
use
훅은 조건부로 Context로부터 값을 읽어오는 if
문 안에서 호출되었어요.
주의
useContext처럼 use(context)도 호출한 컴포넌트의 가장 가까운 상위의 context provider를 찾아내요. use(context)는 위로 탐색하고 해당 훅을 호출한 컴포넌트 내부의 context provider는 고려하지 않아요.
import { createContext, use } from "react";
const ThemeContext = createContext(null);
export default function Usage_1() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
return (
<Panel title="welcome">
<Button show={true}>Sign up</Button>
<Button show={true}>Log in</Button>
</Panel>
);
}
function Panel({ title, children }) {
const theme = use(ThemeContext);
const className = "panel-" + theme;
return (
<section className={className}>
<h1>{title}</h1>
{children}
</section>
);
}
function Button({ show, children }) {
if (show) {
const theme = use(ThemeContext);
const className = "button-" + theme;
return <button className={className}>{children}</button>;
}
return false;
}
Streaming data from the server to the client | 서버에서 클라이언트로 데이터 스트림하기
props를 통해 Promise를 서버 컴포넌트에서 클라이언트 컴포넌트로 전달하는 방식으로 데이터를 서버에서 클라이언트로 스트림해요.
import { fetchMessage } from './lib.js';
import { Message } from './message.js';
export default function App() {
const messagePromise = fetchMesaage();
return (
<Suspense fallback={<p>waiting for message...</>}>
<Message messagePromise={messagePromise}/>
</Suspense>
)
}
그리고나면 클라이언트 컴포넌트는 prop으로 전달받은 Promise를 use
훅으로 전달해요. 이것은 클라이언트 컴포넌트가 서버 컴포넌트에서 맨 처음 만들어진 Promise의 값을 읽게 해줘요.
// message.js
'use client';
import { use } from 'react';
export function Message({ messagePromise }) {
const messageContent = use(messagePromise);
return <p>Here is the message: {messageContent}</p>;
}
Message
컴포넌트는 Suspense
로 감싸져있기 때문에 fallback은 Promise
가 해결될 때까지 화면에 표시돼요.
Promise
가 해결되면 use
훅에서 값을 읽어들이고 Message
컴포넌트는 Suspense fallback을 대체해요.
"use client";
import { use, Suspense } from "react";
function Message({ messagePromise }) {
const messageContent = use(messagePromise);
return <p>Here is the message: {messagePromise}</p>
}
export function MessageContainer({ messagePromise }) {
return (
<Suspense fallback={<p>⌛Downloading message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
)
}
NOTE
Promise를 서버 컴포넌트에서 클라이언트 컴포넌트로 전달할 때, Promise의 해결된 값은 서버와 클라이언트 사이에서 전달하기 위해 직렬화가 가능해야해요. function과 같은 데이터 타입은 직렬화가 불가능하고 Promise와 같은 해결된 값이 될 수 없어요.
더 알아보기
서버 또는 클라이언트 컴포넌트의 Promise를 해결해야하나요?
Promise는 서버컴포넌트에서 클라이언트 컴포넌트로 전달될 수 있고 클라이언트 컴포넌트에서 use
훅을 통하여 해결될 수 있어요. 서버 컴포넌트에서도 await
를 이용하여 Promise를 해결하고 prop으로 필요한 데이터를 클라이언트 컴포넌트로 전달할 수 있어요.
하지만 서버 컴포넌트에서 await
를 사용하는 것은 await
구문이 끝날 때까지 컴포넌트의 렌더링을 막아요. Promise를 서버 컴포넌트에서 클라이언트 컴포넌트로 전달하는 것은 Promise를 서버 컴포넌트의 렌더링이 멈추는 것을 방지할 수 있어요.
Dealing with rejected Promises | 거부된 Promise 다루기
몇몇 상황에서 use
로 전달받은 Promise가 거부되기도 해요. 둘 중 하나로 거부된 Promise를 다룰 수 있어요.
- error boundary를 통해 사용자에게 에러를 표시하기
Promise.catch
를 통해 대체 값을 제공하기
주의
use훅은 try-catch 블록 안에서는 호출될 수 없어요. try-catch 블록 대신에, Error Boundary로 컴포넌트를 감싸거나 Promise의 catch 메서드를 이용하여 대체 값을 제공하세요.
error boundary를 통해 사용자에게 에러를 표시하기
Promise가 거부될 때, 사용자에게 error를 보여주고 싶다면 error boundary를 사용할 수 있어요. error boundary를 사용하기 위해서는 use
훅을 부르는 컴포넌트를 error boundary로 감싸야해요. use
훅을 전달하는 Promise가 거부된다면, error boundary를 위한 fallback이 보여져요.
"use client";
import { use, Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";
export function MessageContainer({ messagePromise }) {
return (
<ErrorBoundary fallback={<p>⚠️Something went wront</p>}>
<Suspense fallback={<p>⌛Downloading message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
</ErrorBoundary>
)
}
function Message({ messagePromise }) {
const messageContent = use(messagePromise);
return <p>Here is the message: {content}</p>
}
Promise.catch
를 통해 대체 값을 제공하기
use
훅으로 전달받은 Promise가 거부될 때, 사용자에게 대체 값을 제공하고 싶다면, Promise의 catch
메서드를 사용할 수 있어요.
import { Message } from './message.js';
export default function App() {
const messagePromise = new Promise((resolve, reject) => {
reject();
}.catch(() => {
return "no new message found.";
});
return (
<Suspense fallback={<p>waiting for message...</>}>
<Message messagePromise={messagePromise}/>
</Suspense>
)
}
Promise의 catch
메소드를 사용하기 위해서는 Promise 객체 내에서 catch
를 호출하세요. catch
는 하나의 인자를 받아요: 에러 메시지 받는 함수를 인자로 받아요. catch
로 전달되는 함수에서 무엇이 반환되어도 Promise의 해결된 값으로 사용될 거예요.
Troubleshooting | 트러블슈팅
"Suspense Exception: 이건 진짜 에러가 아니에요!"
당신은 리액트 컴포넌트나 훅 함수 밖에서 use
훅을 호출하거나 try-catch 블록에서 use
훅을 호출하고 있어요. 만약 use
훅을 try-catch 블록 안에서 호출했다면 컴포넌트를 error boundary로 감싸거나 에러를 잡고 다른 값으로 Promise를 해결하기 위하여 Promise의 catch
메서드를 호출하세요. 이 예시를 참고하세요.
만약 use
훅을 리액트 컴포넌트나 Hook 함수 외부에서 호출한다면 use
훅의 호출 위치를 리액트 컴포넌트나 Hook 함수 내부로 옮기세요.
function MessageComponent({messagePromise}) {
function download() {
// ❌ the function calling `use` is not a Component or Hook
const message = use(messagePromise);
// ...
}
}
대신 use
훅을 호출하는 함수가 컴포넌트나 훅인 클로저 컴포넌트 밖에서 호출하세요.
function MessageComponent({messagePromise}) {
// ✅ `use` is being called from a component.
const message = use(messagePromise);
// ...
}
'resolve' 번역 추가 설명
한국어로의 번역 과정에서 'resolve'를 '해결하다'로 번역하였습니다. (ex. resolved Promise => 해결된 프로미스) MDN의 문서에 따르면 Promise에서 사용되는 'resolve'란 해당 Promise가 대기 상태가 아니라 이행되었거나 거부된 '처리'된 상태 또는 다른 프로미스의 상태에 맞춰 '잠겨진' 상태를 의미합니다. 즉, 더 이상의 처리 혹은 거부가 해당 프로미스에 영향을 미치지 못합니다.
더 자세한 설명을 원하시면 MDN의 Promise 부분과 States and fates를 참고하세요.
'리액트 공식문서 | React Docs > Reference > react@18.2.0' 카테고리의 다른 글
[Hooks] useDebugValue | useDebugValue 훅 (0) | 2024.01.11 |
---|---|
[Hooks] useContext | useContext 훅 (2) | 2024.01.07 |
[Hooks] useCallback | useCallback훅 (2) | 2024.01.04 |
[Hooks] Hooks | 리액트 내장 훅 (0) | 2023.12.22 |
[Overview] React Reference Overview | 리액트 공식문서 개요 (0) | 2023.12.19 |