use¶
use - это хук React, который позволяет вам прочитать значение ресурса, например промиса или контекста.
1 | |
Описание¶
use(resource)¶
Вызовите use в вашем компоненте, чтобы прочитать значение ресурса, например Promise или context.
1 2 3 4 5 6 7 | |
В отличие от всех остальных хуков 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(context)¶
Вызовите use с контекстом, чтобы прочитать его значение. В отличие от useContext, use можно вызывать в циклах и условиях вроде if.
1 2 3 4 5 | |
Параметры¶
context: контекст, созданный черезcreateContext.
Возвращаемое значение¶
Значение контекста для переданного контекста. Его определяет ближайший провайдер контекста выше компонента, который вызвал use. Если провайдера нет, возвращается defaultValue, переданный в createContext.
Предупреждения¶
useнужно вызывать внутри компонента или хука.- Чтение контекста через
useне поддерживается в серверных компонентах.
use(promise)¶
Вызовите use с промисом, чтобы прочитать его разрешённое значение. Компонент, который вызывает use, приостанавливается, пока промис ожидает. Несмотря на имя, use — это не хук. В отличие от хуков, его можно вызывать в циклах и условиях вроде if.
1 2 3 4 5 | |
Если компонент, вызывающий use, обёрнут в границу Suspense, пока промис ожидает, будет показан фолбэк. Когда промис разрешится, фолбэк Suspense заменится компонентами, которые рендерятся с данными, возвращёнными use. Если промис отклонён, будет показан фолбэк ближайшей границы ошибки.
Параметры¶
promise: промис, разрешённое значение которого нужно прочитать. Промис должен быть закэширован, чтобы между повторными рендерами переиспользовался один и тот же экземпляр.
Возвращаемое значение¶
Разрешённое значение промиса.
Предупреждения¶
useнужно вызывать внутри компонента или хука.useнельзя вызывать внутри блока try-catch. Вместо этого оберните компонент в границу ошибки, чтобы поймать ошибку и показать фолбэк.- Промисы, переданные в
use, нужно кэшировать, чтобы между повторными рендерами переиспользовался один и тот же экземпляр. Смотрите кэширование промисов ниже. - Когда промис передаётся из серверного компонента в клиентский, его разрешённое значение должно быть сериализуемым.
use(browser())¶
Вызовите use со значением, которое вернул browser, в компоненте, который должен рендериться только в браузере:
1 2 3 4 5 6 7 | |
Во время серверного рендеринга компонент, вызывающий use(browser()), приостанавливается, и React включает в HTML фолбэк ближайшей границы <Suspense>. В браузере use(browser()) возвращает undefined, и компонент рендерится как обычно.
Параметры¶
browserValue: значение, которое вернулbrowser.
Возвращаемое значение¶
В браузере use(browser()) возвращает undefined.
Предупреждения¶
- Компонент, вызывающий
use(browser()), во время серверного рендеринга должен быть внутри границы<Suspense>. Без неё серверный рендеринг завершится ошибкой. - В приложении с React Server Components
use(browser())нужно вызывать из клиентского компонента, а не из серверного компонента.
Использование¶
Использование (контекст)¶
Чтение контекста с помощью use¶
Когда в use передается context, он работает аналогично useContext. В то время как useContext должен вызываться на верхнем уровне вашего компонента, use можно вызывать внутри условий типа if и циклов типа for. use предпочтительнее, чем useContext, потому что он более гибкий.
1 2 3 4 5 6 | |
use возвращает значение контекста для переданного вами контекста. Чтобы определить значение контекста, React просматривает дерево компонентов и находит ближайший провайдер контекста выше для данного контекста.
Чтобы передать контекст кнопке Button, оберните ее или один из ее родительских компонентов в соответствующий провайдер контекста.
1 2 3 4 5 6 7 8 9 10 11 | |
Не имеет значения, сколько слоев компонентов находится между провайдером и Button. Когда Button в любом месте внутри Form вызывает use(ThemeContext), она получит "dark" в качестве значения.
В отличие от useContext, use можно вызывать в условиях и циклах, как if.
1 2 3 4 5 6 7 | |
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 | |
Чтение промиса из контекста¶
Чтобы делиться асинхронными данными без прокидывания пропсов, положите промис в значение контекста, прочитайте его через use(context) и разрешите через use(promise):
1 2 3 4 5 6 7 8 | |
Чтобы прочитать значение, нужны два вызова use, потому что само значение контекста не ожидается. Прежде чем тянуться к контексту, смотрите альтернативы в разделе Прежде чем использовать контекст.
Оберните компоненты, которые читают промис, в границу Suspense, чтобы приостановилось только это поддерево, пока промис ожидает. Подробнее о чтении промисов через use — в разделе Использование (промисы) ниже.
Подводный камень
Если этот приём используется с серверными компонентами, повторная загрузка промиса требует повторного рендера серверного компонента, который кладёт промис в контекст. Не ставьте промис в контекст высоко в дереве: иначе без нужды заново отрендерится большая часть приложения.
Использование (промисы)¶
Чтение промиса с помощью use¶
Вызовите use с промисом, чтобы прочитать его разрешённое значение. Пока промис ожидает, компонент приостановится.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Оберните компонент, который вызывает use, в границу Suspense, чтобы React мог показать фолбэк, пока промис ожидает. Ближайшая граница Suspense выше приостановленного компонента показывает свой фолбэк. Когда промис разрешится, React прочитает значение через use и заменит фолбэк отрендеренным компонентом.
Примеры¶
Загрузка данных с помощью use¶
В этом примере Albums вызывает use с закэшированным промисом. Пока промис ожидает, компонент приостанавливается, и React показывает ближайший фолбэк Suspense. Отклонённые промисы доходят до ближайшей границы ошибки.
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 | |
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 45 | |
1 2 3 4 5 6 7 8 9 | |
Решение
До 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 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
Решение
Промисы, переданные в use, нужно кэшировать
Промисы, созданные во время рендера, создаются заново при каждом рендере. Из-за этого React снова и снова показывает фолбэк Suspense, и содержимое не появляется.
1 2 3 4 5 | |
Вместо этого передайте промис из кэша, фреймворка с поддержкой Suspense или серверного компонента:
1 2 | |
Почему промисы создаются заново при каждом рендере?
React не сохраняет состояние рендеров, которые приостановились до монтирования. После каждой приостановки React заново пробует рендер с нуля, поэтому любой промис, созданный во время рендера, создаётся снова.
Промис легко нечаянно пересоздать во время рендера вот так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
В идеале промисы создают до рендера: в обработчике события, загрузчике маршрута или серверном компоненте — и передают в компонент, который вызывает use. Ленивая загрузка во время рендера откладывает сетевые запросы и может создать водопады.
1 2 | |
Кэширование промисов для клиентских компонентов¶
Промисы, переданные в use в клиентских компонентах, нужно кэшировать, чтобы между повторными рендерами переиспользовался один и тот же экземпляр. Если новый промис создаётся прямо в рендере, React будет показывать фолбэк Suspense при каждом повторном рендере.
1 2 3 4 5 6 7 8 9 | |
Функция fetchData возвращает один и тот же промис при каждом вызове с тем же URL. Когда use при повторном рендере получает тот же промис, он синхронно читает уже разрешённое значение и не приостанавливается.
Примечание
Как кэшировать промисы, зависит от фреймворка, с которым вы используете Suspense. Обычно у фреймворков есть встроенное кэширование. Если фреймворка нет, подойдёт простой кэш на уровне модуля, как выше, или источник данных с поддержкой Suspense.
В примере ниже клик по «Re-render» обновляет состояние в App и вызывает повторный рендер. Поскольку fetchData возвращает тот же закэшированный промис, Albums читает значение синхронно и не показывает фолбэк Suspense снова.
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 | |
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 | |
Как реализовать кэш промисов
Простой кэш хранит промис по ключу URL, чтобы между рендерами переиспользовался один экземпляр. Чтобы не показывать лишний фолбэк Suspense, когда данные уже есть, на промисе можно выставить поля status и value (или reason). React смотрит эти поля, когда вызывается use: если status равен 'fulfilled', он синхронно читает value и не приостанавливается. Если status равен 'rejected', он бросает reason. Если поля нет или оно равно 'pending', он приостанавливается.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Это в первую очередь нужно авторам библиотек, которые строят слой данных, совместимый с Suspense. React сам выставит поле status у промисов, где его нет, но если выставить его заранее, лишний рендер не случится, когда данные уже доступны.
Этот кэш — основа для повторной загрузки данных (смена ключа кэша запускает новую загрузку) и предзагрузки при наведении (ранний вызов fetchData значит, что к моменту чтения через use промис уже может быть разрешён).
Не пропускайте вызов use из-за того, что промис уже завершился
В отличие от других хуков, use можно вызывать в условиях и циклах, но для самого промиса его нужно вызывать всегда. Никогда не читайте promise.status или promise.value напрямую, чтобы обойти use: всегда передавайте промис в use и дайте React разобраться.
1 2 3 4 5 | |
1 2 | |
Такой обход может сломать оптимизации Suspense и возможности Suspense в React DevTools. use(promise) можно вызывать условно, но нельзя решать, вызывать ли use(promise), по самому промису.
Повторная загрузка данных в клиентских компонентах¶
Чтобы обновить данные по тому же URL (например, кнопкой «Refresh»), сбросьте запись кэша и начните новую загрузку внутри startTransition. Сохраните полученный промис в состоянии, чтобы вызвать повторный рендер. Пока новый промис ожидает, React продолжает показывать текущее содержимое, потому что обновление внутри перехода.
1 2 3 4 5 6 7 8 9 10 11 | |
refetchData очищает старую запись кэша и начинает новую загрузку по тому же URL. Сохранение полученного промиса в состоянии вызывает повторный рендер внутри перехода. При повторном рендере Albums получает новый промис, и use приостанавливается на нём, а React продолжает показывать старое содержимое.
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 45 46 47 48 49 | |
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 45 46 47 48 49 50 51 52 53 54 | |
1 | |
Примечание
Фреймворки с поддержкой Suspense обычно дают свои механизмы кэша и инвалидации. Свой кэш выше полезен, чтобы понять приём, но на практике лучше решение для загрузки данных вашего фреймворка.
Предзагрузка данных при наведении¶
Данные можно начать грузить до того, как они понадобятся, вызвав fetchData при наведении. Поскольку fetchData кэширует промис, к моменту клика данные уже могут быть на месте. Если к моменту чтения через use промис разрешился, React сразу рендерит компонент и не показывает фолбэк Suspense.
1 2 3 4 5 6 7 8 | |
В этом примере наведение на кнопку исполнителя начинает в фоне грузить его альбомы. Если не наводить курсор заранее, клик показывает фолбэк загрузки. Подержите курсор на кнопке немного перед кликом и сравните.
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 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | |
1 | |
Потоковая передача данных от сервера к клиенту¶
Данные могут передаваться от сервера к клиенту путем передачи Promise в качестве реквизита от серверного компонента к клиентскому компоненту.
1 2 3 4 5 6 7 8 9 10 11 | |
Затем клиентский компонент принимает полученное обещание в качестве реквизита и передает его хуку use. Это позволяет клиентскому компоненту прочитать значение из обещания, которое было первоначально создано серверным компонентом.
1 2 3 4 5 6 7 8 9 | |
Поскольку 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 | |
Сериализуемость значений
При передаче Promise от серверного компонента к клиентскому компоненту его разрешенное значение должно быть сериализуемым для передачи между сервером и клиентом. Типы данных, такие как функции, не являются сериализуемыми и не могут быть разрешенным значением такого промиса.
Как разрешить промис в серверном или клиентском компоненте?
Промис можно передать из серверного компонента в клиентский компонент и разрешить его в клиентском компоненте с помощью хука use. Вы также можете разрешить промис в серверном компоненте с помощью await и передать необходимые данные клиентскому компоненту в качестве свойства.
1 2 3 4 | |
Но использование await в компоненте Server Component заблокирует его рендеринг до завершения оператора await. Передача промиса от серверного компонента клиентскому компоненту не позволяет промису блокировать отрисовку серверного компонента.
Отображение ошибки с помощью границы ошибки¶
Если промис, переданный в use, отклонён, ошибка всплывает к ближайшей границе ошибки. Оберните компонент, который вызывает use, в границу ошибки, чтобы показать фолбэк, когда промис отклонён.
В примере ниже fetchData отклоняется при первой попытке и успешно завершается при повторе. Граница ошибки перехватывает отказ и показывает фолбэк с кнопкой «Try again».
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 | |
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 45 46 47 48 49 | |
1 2 3 4 5 6 7 8 9 | |
Использование (браузер)¶
Рендер компонента только в браузере¶
Передайте в use значение, которое вернул browser, внутри компонента, который должен рендериться только в браузере.
Нажмите Reload, чтобы увидеть фолбэк загрузки в исходном HTML. После гидратации React показывает черновик, загруженный из localStorage.
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 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
1 2 3 4 5 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Во время серверного рендеринга use(browser()) приостанавливает компонент, и React включает в HTML фолбэк ближайшей границы приостановки. В браузере use(browser()) возвращает undefined, и сохранённый черновик рендерится как обычно.
Работа с отклоненными промисами¶
В некоторых случаях промис, переданный в use, может быть отклонен. Вы можете обработать отклоненные промисы следующим образом:
- Отображение ошибки для пользователей с границей ошибки.
- Предоставить альтернативное значение с помощью
Promise.catch
try-catch
use нельзя вызывать в блоке try-catch. Вместо блока try-catch оберните ваш компонент в границу ошибки или предоставьте альтернативное значение для использования в методе .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 | |
Предоставление альтернативного значения с помощью Promise.catch¶
Если вы хотите предоставить альтернативное значение, когда промис, переданный в use, будет отклонен, вы можете использовать метод catch промиса.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
Чтобы использовать метод 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 | |
Вместо этого вызывайте use вне закрытий компонентов, если функция, вызывающая use, является компонентом или хуком.
1 2 3 4 | |
Предупреждение: «A component was suspended by an uncached promise»¶
Промис, переданный в use, не закэширован, поэтому React не может переиспользовать его между повторными рендерами.
Так часто бывает, если fetch или async-функцию вызывают прямо в рендере:
1 2 3 4 5 | |
Чтобы это исправить, закэшируйте промис, чтобы переиспользовался один и тот же экземпляр:
1 2 | |
Подробнее в разделе Кэширование промисов для клиентских компонентов.
Источник — https://react.dev/reference/react/use