TLDR
Use let's you read the value of a Promise or a Context inside a component
const value = use(resource)
A short example we can use it to read context or promise values like so:
import { use } from "react";
function MessageComponent ({ messagePromise }) {
const message = use(messagePromise);
const theme = use(ThemeContext);
}
.
Create a new file called lib.ts:
// lib.ts
'use server';
export const fetchMessage = async () => {
await new Promise((resolve) => setTimeout(resolve, 2500));
return 'Secret Message Recieved!';
};
We're "faking" an API request here, in a real app this would be a call to a database or a fetch request to some API to grab some external data. But by using an await, Promise() and setTimeout we can make our Promise last 2.5 seconds in this example.
We need to create a couple of components to get this working, a wrapper that'll use Suspense then inside that component and another component that'll use use to grab the result of our fetchMessage() Promise we just created in lib.ts.
Create these two new files Components/MessageContainer.tsx & Components/PromiseExample.tsx:
MessageContaienr.tsx:
// MessageContainer.tsx
import { Suspense } from 'react';
import PromiseExample from './PromiseExample';
export default function MessageContainer({
messagePromise,
}: {
messagePromise: Promise<string>;
}) {
return (
<Suspense fallback={<p>Waiting for the message...</p>}>
<PromiseExample messagePromise={messagePromise} />
</Suspense>
);
}
The <p> inside the fallback is what will display while we wait for our Promise to resolve.
Now for the PromiseExample.tsx file:
// PromiseExample
'use client';
import { use } from 'react';
export default function PromiseExample({
messagePromise,
}: {
messagePromise: Promise<string>;
}) {
const messageConent = use(messagePromise);
return <p>Here's the message: {messageConent}</p>;
}
Here's where the magic happens, we're using the use component to get the resolved promise value, the string "Secret Message Recieved" we return in our fetchMessage() Promise. How easy is that? use() and <Suspense> sure make working with Asynchronous data a breeze...
Let's hook up our MessageContainer to the App.tsx so we can see it in action!
App.tsx:
// App.tsx
// Other imports...
import { fetchMessage } from './lib';
import MessageContainer from './components/MessageContainer';
function App() {
const { theme } = use(ThemeContext);
const [messagePromise, setMessagePromise] = useState<Promise<string> | null>(
null
);
const handleFetchMessage = () => {
setMessagePromise(fetchMessage());
};
return (
<div className={`App ${theme}`}>
<ContextExample />
<button onClick={() => handleFetchMessage()}>Send Message</button>
{messagePromise && <MessageContainer messagePromise={messagePromise} />}
</div>
)
}
export default App;
We create a new state object to store the messagePromise, defaulting it to null. This is so we can hide the message if we haven't requested it yet.
The handleFetchMessage() function simply sets the messagePromise state to contain the message Promise from our fetchMessage server action. We created a <button> to call handleFetchMessage. Finally, we conditionally show <MessageCotnainer /> on our FE depending on the state of messagePromise, this means it will only show our <MessagePromiseContainer /> component once the fetchMessage() promise has been initiated.
So after hitting this button: <button onClick={() => handleFetchMessage()}>Send Message</button>. We call our fetchMessage() server action, the fallback in the Suspense is triggered while use() waits for the Promise to be resolved.
Phew... that was a lot, I hope it makes sense! The key part to remember while working with use and Promises is to wrap the Component you're using use within a Suspense and after that, it's as simple as calling use(promiseIWantTheValueOf) <-- Awful variable name right?
Conclusion
Use, unlike React Hooks, can be used inside an if or for statement. However, use can Only be used inside a Component or Hook. I can see this being handy when you have different privileges in your application. You may have one API req for Admins and another for normal users. Before you couldn't call React hooks conditonally, however use is breaking the mold here.
So you can do something like:
if (isAdmin) {
use(getAllUsers)
} else {
use(getUsersInMyAccount)
}
^ Please comment below if you can think of better examples of this new superpower of being able to conditionally call the hook.
React sure is making working with servers and asynchronous code easier, with a focus towards SSR and working with forms, I'm all for it.
If you read this far I'm very impressed! I didn't think it would take this much explaining but I think it's important to see where/how this hook is going to be useful.
Thank you all and if you want to support me, please give my YouTube Channel a look as I look to go more in-depth into everything React & Web Dev in general!
SOCIAL SHARE CARD GENERATOR