Easy React data fetching with `use()`

Posted on Sat May 27 2023
react nextjs

**NOTE: ** : 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 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 and this is not a useEffect tutorial so a quick response would just be - because, React. 😅

My point is, this is not intuitive code to read but most React developers, we 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 "@/fetchUsers";

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 use hooks conditionally.

Here is the 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 libaries 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 being a point of confusion for beginner React devs.