👉 Full Stack Developer / Learning In Public / Building For Fun

react webdev nextjs

Easy React data fetching with the new `use()` hook

React.use() is still an unstable API. For more information check out the support for promises React RFC . At the time of writing this, you can only test this API in Next.js 13.

OK with that disclaimer out the way, lets talk about how use() works .

In short, this new hook will let you you run asynchronous code in your client-side react components. You can think of it as the React version of async / await.

To appreciate why it’s going to be a game changer for client side data-fetching, lets compare the old-way of doing a client-side data fetch using useEffect() with use().

// returns an array of user objects e.g:
// [ {id:1 name: "Leanne Graham"}, {id:1 name: "Ervin Howell"}] 

export const fetchUsers = async () => {
  const resp = await 
  fetch("https://jsonplaceholder.typicode.com/users");
  const json = await resp.json();
  return json; 
};
'use client'

import { useState, useEffect } from "react";
import { fetchUsers } from "@/fetch-users";


export const OldDataFetchData = () => {

const [usersData, setUsersData] = useState(null);


useEffect(() => {
	if (!usersData) {
		const startFetchingData = async () => {
			const users = await fetchUsers();
			setUsersData(users);
		};
	       startFetchingData();
	}

	}, [usersData]);

  

	if (!usersData) {
		return <>Loading...</>;
	}

	return (
		<>
			{usersData.map((u) => (
				<div key={u.id}>{u.name} </div>
			))}
		
		</>
	);

};

This is idiomatic code for most React developers. But for those less familiar with React and useEffect you may ask all sort of questions like:

There are technical answers to all of these,but this is not a useEffect tutorial so a quick response would be - because, React. 😅

My point is, this is not intuitive code to read but we React developers have become accustomed to the rules and quirks of useEffect.

Now lets see how can re-write this with use() .

'use client'

import { use } from "react";
import { fetchUsers } from "@/fetch-users";

export const NewDataFetchData = () => {

const usersData = use(fetchUsers());

return (
	<>
		{usersData.map((u) => (
			<div key={u.id}>{u.name} </div>
		))}
	</>
);

};

Thats’s it!

Ok I lied.

A little bit more code is required If you want to handle a loading state. In the parent server component that uses this client component, you can wrap it in Suspense which can accept a React component in its fallback prop.

// ./app/page.jsx

import { Suspense } from "react";
import { NewDataFetchData } from "@/components/NewDataFetch";  

export default function Home() {
	return (
		<main>
		    <Suspense fallback={<>Loading...</>}>
				<NewDataFetchData />
			</Suspense>
		</main>
	);
}

Overall, much more elegant to read and write.


An interesting property of the use hook is that it can be called conditionally 🤯.

This is a big deal because we’ve traditionally never been able to usehooks conditionally.

Here is an example of how this could look taken from the rfc.

function Note({id, shouldIncludeAuthor}) {
  const note = use(fetchNote(id));

  let byline = null;
  if (shouldIncludeAuthor) {
    const author = use(fetchNoteAuthor(note.authorId));
    byline = <h2>{author.displayName}</h2>;
  }

  return (
    <div>
      <h1>{note.title}</h1>
      {byline}
      <section>{note.body}</section>
    </div>
  );
}

I’m excited for this hook to become stable but I have mixed emotions.

On one hand, the ergonomics of data fetching will be so much better and we may no longer need to rely of a third party libraries like TanStack query.

On the other hand, our mental model of hooks needs to adjust when using use(), maybe this is not a big deal, but I can see this being a point of confusion for beginner React devs.

If you want to test out the use() for yourself, feel free to fork this codesandbox which is running the code examples above.

{% codesandbox quirky-ishizaka-jg31he %}