Перейти к содержанию

use

Canary

В настоящее время хук use доступен только в канале React canary и экспериментальном канале.

use - это хук React, который позволяет вам прочитать значение ресурса, например промиса или контекста.

1
const value = use(resource);

Описание

use(resource)

Вызовите use в вашем компоненте, чтобы прочитать значение ресурса, например Promise или context.

1
2
3
4
5
6
7
import { use } from 'react';

function MessageComponent({ messagePromise }) {
    const message = use(messagePromise);
    const theme = use(ThemeContext);
    // ...
}

В отличие от всех остальных хуков React, use можно вызывать в циклах и условных операторах, таких как if. Как и другие хуки React, функция, вызывающая use, должна быть компонентом или хуком.

При вызове с Promise хук use интегрируется с Suspense и error boundaries. Компонент, вызывающий use, приостанавливается на время выполнения Promise, переданного use. Если компонент, вызывающий use, обернут в границу Suspense, то будет отображен fallback. После разрешения Promise, Suspense fallback заменяется рендерингом компонентов, использующих данные, возвращенные хуком use. Если промис, переданный в use, отклонен, будет отображен фаллбек ближайшей границы ошибки.

Параметры

  • resource: это источник данных, из которого вы хотите прочитать значение. Ресурсом может быть промис или контекст.

Возвращает

Хук use возвращает значение, которое было прочитано из ресурса, как разрешенное значение промиса или контекст.

Замечания

  • Хук use должен быть вызван внутри компонента или хука.
  • При получении данных в серверном компоненте отдавайте предпочтение async и await, а не use. async и await выполняют рендеринг с того момента, когда был вызван await, в то время как use повторно рендерит компонент после разрешения данных.
  • Предпочтительнее создавать промисы в Компонентах сервера и передавать их в Компоненты клиента, чем создавать обещания в компонентах клиента. Обещания, созданные в клиентских компонентах, пересоздаются при каждом рендере. Обещания, переданные из серверного компонента в клиентский компонент, стабильны при каждом рендере. См. этот пример.

Использование

Чтение контекста с помощью use

Когда в use передается context, он работает аналогично useContext. В то время как useContext должен вызываться на верхнем уровне вашего компонента, use можно вызывать внутри условий типа if и циклов типа for. use предпочтительнее, чем useContext, потому что он более гибкий.

1
2
3
4
5
6
import { use } from 'react';

function Button() {
    const theme = use(ThemeContext);
    // ...
}

use возвращает значение контекста для переданного вами контекста. Чтобы определить значение контекста, React просматривает дерево компонентов и находит ближайший провайдер контекста выше для данного контекста.

Чтобы передать контекст кнопке Button, оберните ее или один из ее родительских компонентов в соответствующий провайдер контекста.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function MyPage() {
    return (
        <ThemeContext.Provider value="dark">
            <Form />
        </ThemeContext.Provider>
    );
}

function Form() {
    // ... renders buttons inside ...
}

Не имеет значения, сколько слоев компонентов находится между провайдером и Button. Когда Button в любом месте внутри Form вызывает use(ThemeContext), она получит "dark" в качестве значения.

В отличие от useContext, use можно вызывать в условиях и циклах, как if.

1
2
3
4
5
6
7
function HorizontalRule({ show }) {
    if (show) {
        const theme = use(ThemeContext);
        return <hr className={theme} />;
    }
    return false;
}

use вызывается внутри оператора if, позволяя вам условно считывать значения из Context.

Ближайший провайдер

Как и useContext, use(context) всегда ищет ближайшего провайдера контекста выше компонента, который его вызывает. Он ищет вверх и не рассматривает провайдеров контекста в компоненте, из которого вы вызываете use(context).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import { createContext, use } from 'react';

const ThemeContext = createContext(null);

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

function Form() {
    return (
        <Panel title="Welcome">
            <Button show={true}>Sign up</Button>
            <Button show={false}>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;
}

Потоковая передача данных от сервера к клиенту

Данные могут передаваться от сервера к клиенту путем передачи Promise в качестве реквизита от серверного компонента к клиентскому компоненту.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import { fetchMessage } from './lib.js';
import { Message } from './message.js';

export default function App() {
    const messagePromise = fetchMessage();
    return (
        <Suspense fallback={<p>waiting for message...</p>}>
            <Message messagePromise={messagePromise} />
        </Suspense>
    );
}

Затем клиентский компонент принимает полученное обещание в качестве реквизита и передает его хуку use. Это позволяет клиентскому компоненту прочитать значение из обещания, которое было первоначально создано серверным компонентом.

1
2
3
4
5
6
7
8
9
// 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 не будет разрешен. Когда обещание будет разрешено, значение будет считано хуком use и компонент Message заменит фаллбэк Suspense.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
'use client';

import { use, Suspense } from 'react';

function Message({ messagePromise }) {
    const messageContent = use(messagePromise);
    return <p>Here is the message: {messageContent}</p>;
}

export function MessageContainer({ messagePromise }) {
    return (
        <Suspense
            fallback={<p>Downloading message...</p>}
        >
            <Message messagePromise={messagePromise} />
        </Suspense>
    );
}

Сериализуемость значений

При передаче Promise от серверного компонента к клиентскому компоненту его разрешенное значение должно быть сериализуемым для передачи между сервером и клиентом. Типы данных, такие как функции, не являются сериализуемыми и не могут быть разрешенным значением такого промиса.

Как разрешить промис в серверном или клиентском компоненте?

Промис можно передать из серверного компонента в клиентский компонент и разрешить его в клиентском компоненте с помощью хука use. Вы также можете разрешить промис в серверном компоненте с помощью await и передать необходимые данные клиентскому компоненту в качестве свойства.

1
2
3
4
export default function App() {
    const messageContent = await fetchMessage();
    return <Message messageContent={messageContent} />
}

Но использование await в компоненте Server Component заблокирует его рендеринг до завершения оператора await. Передача промиса от серверного компонента клиентскому компоненту не позволяет промису блокировать отрисовку серверного компонента.

Работа с отклоненными промисами

В некоторых случаях промис, переданный в use, может быть отклонен. Вы можете обработать отклоненные промисы следующим образом:

  1. Отображение ошибки для пользователей с границей ошибки.
  2. Предоставить альтернативное значение с помощью Promise.catch

Отображение ошибки для пользователей с границей ошибки

Если вы хотите отобразить ошибку для пользователей, когда промис отклоняется, вы можете использовать границу ошибки. Чтобы использовать границу ошибки, оберните компонент, в котором вы вызываете хук use, в границу ошибки. Если промис, переданный в use, будет отклонен, то будет отображен обратный вариант границы ошибки.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
'use client';

import { use, Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

export function MessageContainer({ messagePromise }) {
    return (
        <ErrorBoundary
            fallback={<p>⚠️Something went wrong</p>}
        >
            <Suspense
                fallback={<p>Downloading message...</p>}
            >
                <Message messagePromise={messagePromise} />
            </Suspense>
        </ErrorBoundary>
    );
}

function Message({ messagePromise }) {
    const content = use(messagePromise);
    return <p>Here is the message: {content}</p>;
}

Предоставление альтернативного значения с помощью Promise.catch

Если вы хотите предоставить альтернативное значение, когда промис, переданный в use, будет отклонен, вы можете использовать метод catch промиса.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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...</p>}>
            <Message messagePromise={messagePromise} />
        </Suspense>
    );
}

Чтобы использовать метод catch промиса, вызовите catch на объекте промиса. catch принимает единственный аргумент: функцию, которая принимает в качестве аргумента сообщение об ошибке. То, что будет возвращено функцией, переданной в catch, будет использовано в качестве разрешенного значения промиса.

Устранение неполадок

"Suspense Exception: This is not a real error!"

Вы либо вызываете use вне компонента React или функции Hook, либо вызываете use в блоке try-catch. Если вы вызываете use внутри блока try-catch, оберните ваш компонент в границу ошибки или вызовите catch промиса, чтобы поймать ошибку и разрешить промис другим значением. См. эти примеры.

Если вы вызываете use вне компонента React или функции Hook, перенесите вызов use в компонент React или функцию Hook.

1
2
3
4
5
function MessageComponent({messagePromise}) {
  function download() {
    // ❌ the function calling `use` is not a Component or Hook
    const message = use(messagePromise);
    // ...

Вместо этого вызывайте use вне закрытий компонентов, если функция, вызывающая use, является компонентом или хуком.

1
2
3
4
function MessageComponent({messagePromise}) {
  // ✅ `use` is being called from a component.
  const message = use(messagePromise);
  // ...

Источник — https://react.dev/reference/react/use

Комментарии