ReactJS - use Hook



As we know in React a "hook" is a special function that lets us add state and other React features to our functional components. So 'use' is a React Hook that reads the value of a resource, such as a Promise or context. Unlike all other React Hooks, use can be invoked within loops and conditional expressions like if. The function that calls use, like all other React Hooks, must be a Component or Hook.

Basic syntax for use hook

const data = use(asset);

Parameters

asset − It is the source of the data we want to read a value from. So this can be a Promise or a context.

Return Value

This Hook will return the value read from the asset, similar to how a Promise or context's resolved value is returned.

Usage of ‘use’ Hook

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

The ‘use’ hook with Promise

So by using a promise with the ‘use’ hook we can stream data from server to the client. By providing a Promise as a prop from a Server Component to a Client Component, data can be sent from the server to the client.

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>
   );
}

The Client Component then passes the Promise it was given as a prop to the use Hook. This enables the Client Component to read the value from the Promise that the Server Component first produced.

data.js

'use client';

import { use } from 'react';

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

Due to the fact that Data is wrapped in Suspense, the fallback will be shown until the Promise is resolved. When the Promise is resolved, the value is read by the Hook, and the Data component takes the place of the Suspense fallback.

Complete code

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>;
   }
}

Output

dowloading data

How to deal with rejected Promise

Sometimes a Promise passed to ‘use’ hook can be rejected. So we can handle these rejected Promises displaying an error to users with error boundary or by providing a different value with Promise.catch.

Using an error boundary to display an error to users

Assume we want to show our users an error when the Promise is refused, therefore we can use an error boundary. To do this, we can place an error boundary around the component where we are calling the ‘use’ Hook. If the Promise given to use is refused, the error boundary's fallback will be shown.

Example

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;

Output

loading items

Using Promise.catch to provide an alternate value

If the Promise supplied is rejected, we can use the catch method to supply an alternate value.

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>
   );
}

The ‘use’ hook with Context

Let us see another example of the ‘use’ hook. So we will be using the ‘use’ function with a context in React −

In this example we will have a context for managing the theme; it can be light or dark. Then we will have a main app component called MyUseHookApp. It is the top-level component that provides the 'light' theme and will render a form component. Then we will create a form component called MyForm, which is a component that will render a panel and three buttons. After that the panel component is named MyPanel. It will display content with a theme based on the context. And the MyButton component shows a button with a theme as per the context.

Here all the components use the 'use' hook to access the theme from the context and allow them to style elements according to the selected theme.

Example

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;
}

Output

welcome my app

Drawback

Just like useContext, use(context) regularly looks for the nearest context provider above the calling component. It looks up and ignores context providers in the component from which it's calling use(context).

Limitations

  • We should use the 'use' Hooks inside our components or hook.

  • When we are on the server we should always use "async" and "await" to fetch data nicely.

  • If we need promises, make them on the server because they stay steady, but on the client side, they can change a lot.

It is all about making our web pages work better and more efficiently.

reactjs_reference_api.htm
Advertisements