ReactJS - 使用 Hook



众所周知,在 React 中,“hook”是一个特殊的函数,它允许我们将状态和其他 React 功能添加到我们的函数式组件中。“use”是一个 React Hook,它读取资源的值,例如 Promise 或 context。与所有其他 React Hook 不同,“use”可以在循环和条件表达式(如 if)中调用。调用“use”的函数,与所有其他 React Hook 一样,必须是一个组件或 Hook。

“use” Hook 的基本语法

const data = use(asset);

参数

asset − 它是我们要从中读取值的数据源。因此,它可以是 Promise 或 context。

返回值

此 Hook 将返回从 asset 读取的值,类似于返回 Promise 或 context 的已解析值。

‘use’ Hook 的用法

import { use } from 'react';
function TheComponent ({ thePromise }) {
   const msg = use(thePromise);
   const data = use(DataContext);
   // ...

带有 Promise 的 ‘use’ hook

通过使用带有 ‘use’ hook 的 promise,我们可以将数据从服务器流式传输到客户端。通过从服务器组件向客户端组件提供 Promise 作为 prop,可以将数据从服务器发送到客户端。

import { Data } from './data.js';

export default function App() {
   const dataPromise = fetchData();
   return (
      <Suspense fallback={<p>waiting for the data...</p>}>
         <Message dataPromise={dataPromise} />
      </Suspense>
   );
}

然后,客户端组件将作为 prop 提供给它的 Promise 传递给 use Hook。这使客户端组件能够读取服务器组件首先生成的 Promise 中的值。

data.js

'use client';

import { use } from 'react';

export function Data({ dataPromise }) {
   const dataContent = use(dataPromise);
   return <h4>Here is the data: {dataContent}</h4>;
}

由于数据被包装在 Suspense 中,因此在 Promise 解析之前将显示回退内容。当 Promise 解析后,Hook 将读取该值,并且 Data 组件将取代 Suspense 回退内容。

完整代码

Data.js

"use client";

import { use, Suspense } from "react";

function Data({ dataPromise }) {
   const dataContent = use(dataPromise);
   return <h4>Yup!!! Here is the Data: {dataContent}</h4>;
}

export function DataContainer({ dataPromise }) {
   return (
      <Suspense fallback={<p>⌛ Downloading Data...</p>}>
         <Data dataPromise={dataPromise} />
      </Suspense>
   );
}

App.js

import { useState } from "react";
import { DataContainer } from "./data.js";

function fetchData() {
   return new Promise((resolve) => setTimeout(resolve, 2000, "😃"));
}

export default function App() {
   const [dataPromise, setDataPromise] = useState(null);
   const [show, setShow] = useState(false);
   function download() {
      setDataPromise(fetchData());
      setShow(true);
   }
   
   if (show) {
      return <DataContainer dataPromise={dataPromise} />;
   } else {
      return <button onClick={download}>Download Your Data</button>;
   }
}

输出

dowloading data

如何处理被拒绝的 Promise

有时传递给 ‘use’ hook 的 Promise 可能会被拒绝。因此,我们可以通过使用错误边界向用户显示错误或通过使用 Promise.catch 提供不同的值来处理这些被拒绝的 Promise。

使用错误边界向用户显示错误

假设我们希望在 Promise 被拒绝时向用户显示错误,那么我们可以使用错误边界。为此,我们可以将错误边界放在我们调用 ‘use’ Hook 的组件周围。如果提供给 use 的 Promise 被拒绝,则将显示错误边界的回退内容。

示例

ItemListContainer.js

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

export function ItemListContainer({ itemListPromise }) {
   return (
      <ErrorBoundary fallback={<p>⚠ Something went wrong</p>}>
         <Suspense fallback={<p>⌛ Loading items...</p>}>
            <ItemList itemListPromise={itemListPromise} />
         </Suspense>
      </ErrorBoundary>
   );
}

function ItemList({ itemListPromise }) {
   const items = use(itemListPromise);
   return (
   <div>
      <h2>Item List:</h2>
      <ul>
         {items.map((item, index) => (
         <li key={index}>{item}</li>
         ))}
      </ul>
   </div>
   );
}

App.js

import { useState } from "react";
import { ItemListContainer } from "./ItemListContainer";

function fetchItems() {
   return new Promise((resolve) =>
      setTimeout(() => resolve(["Item 1", "Item 2", "Item 3"]), 1000)
   );
}

function App() {
   const [itemListPromise, setItemListPromise] = useState(null);
   const [show, setShow] = useState(false);
   
   function loadItems() {
      setItemListPromise(fetchItems());
      setShow(true);
   }
   
   if (show) {
      return <ItemListContainer itemListPromise={itemListPromise} />;
   } else {
      return <button onClick={loadItems}>Load Items</button>;
   }
}

export default App;

输出

loading items

使用 Promise.catch 提供替代值

如果提供的 Promise 被拒绝,我们可以使用 catch 方法提供替代值。

import { Data } from './data.js';

export default function App() {
   const dataPromise = new Promise((resolve, reject) => {
      reject();
   }).catch(() => {
      return "No data found.";
   });
   
   return (
      <Suspense fallback={<p>waiting for the data...</p>}>
         <Data dataPromise={dataPromise} />
      </Suspense>
   );
}

带有 Context 的 ‘use’ hook

让我们看看 ‘use’ hook 的另一个示例。我们将使用 React 中的 ‘use’ 函数和 context −

在这个例子中,我们将有一个用于管理主题的 context;它可以是浅色或深色。然后我们将有一个名为 MyUseHookApp 的主应用程序组件。它是提供“浅色”主题并渲染表单组件的顶级组件。然后我们将创建一个名为 MyForm 的表单组件,它是一个将渲染面板和三个按钮的组件。之后,面板组件名为 MyPanel。它将根据 context 显示具有主题的内容。MyButton 组件将根据 context 显示具有主题的按钮。

这里所有组件都使用“use”hook 从 context 访问主题,并允许它们根据所选主题设置元素样式。

示例

import { use, createContext } from 'react';

// Create a context for theme
const ThemeContext = createContext(null);

// Main App component.
export default function MyUseHookApp() {
   return (
      // light theme to all components
      <ThemeContext.Provider value="light">
         <MyForm />
      </ThemeContext.Provider>
   )
}

// Form component
function MyForm() {
   return (
      <MyPanel title="Welcome To My App">
         <MyButton show={true}>Join Here</MyButton>
         <MyButton show={true}>Begin</MyButton>
         <MyButton show={false}>Settings</MyButton>
      </MyPanel>
   );
}

// Panel component to display content with a theme
function MyPanel({ title, children }) {
   const theme = use(ThemeContext);
   
   // Apply the theme
   const className = 'panel-' + theme;
   
   return (
      <section className={className}>
         <h1>{title}</h1>
         {children}
      </section>
   )
}

// Button component
function MyButton({ show, children }) {
   if (show) {
      const theme = use(ThemeContext);
      
      // Apply the theme to the component.
      const className = 'button-' + theme;
      
      return (
         <button className={className}>
            {children}
         </button>
      );
   }
   
   // If 'show' is false, return nothing.
   return false;
}

输出

welcome my app

缺点

与 useContext 一样,use(context) 通常会查找调用组件上方最近的 context 提供者。它向上查找并忽略调用 use(context) 的组件中的 context 提供者。

限制

  • 我们应该在组件或 hook 内使用“use”Hook。

  • 当我们在服务器端时,应始终使用“async”和“await”来很好地获取数据。

  • 如果我们需要 promises,请在服务器端创建它们,因为它们保持稳定,但在客户端,它们可能会发生很大变化。

这一切都是为了使我们的网页运行得更好、更高效。

reactjs_reference_api.htm
广告