DEV Community

Ioannis Potouridis
Ioannis Potouridis

Posted on • Edited on

👨‍🔬 I tried experimental React and... 💥💥💥

Hey! 👋

I hope everyone is doing great!

I finally found some time to play with the new experimental React features and especially with Suspense for Data Fetching.

If you are interested in how things work I suggest that you read this first.

Introduction

We all have components that make asynchronous API calls in order to fetch data.

These components usually look like this:

// UserList.js

import React, { useEffect, useState } from "react";

import { fetchUsers } from "./userApi";

function UserList() {
  const [users, setUsers] = useState(null);

  useEffect(() => {
    fetchUsers().then(setUsers);
  }, []);

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

  return (
    <ul>
      {/*  */}
    </ul>
  );
}

UserList component will render out Loading... and fetch the users after that.

React's new features will help us achieve two things.

  1. Avoid writing boilerplate logic for the UI when our data are not ready
  2. Fetch as soon as possible

Installation

In order to experiment with the new features you need to have experimental react and react-dom installed in your project.

npm i react@experimental react-dom@experimental

The very next thing you need to do is make the following changes to your index.js.

//index.js

import React from "react";
// import { render } from "react-dom";
import { createRoot } from "react-dom";

import App from "./App.js";

// render(<App />, document.getElementById("root");
createRoot(document.getElementById("root")).render(<App />);

Example

We need two things to start with:

  1. The Suspense component from React to wrap our component.
  2. A function 1 that will tell our data's state to that Suspense component.

Imagine that this function looks like this.

// helpers.js

export function wrapPromise(promise) {
  // There's no actual magic in it 🧙‍♂️
}

I'm not proud of this part but... here's how I used it.

// userApi.js

import { wrapPromise } from "./helpers";

export function fetchUsers() {
  // I intentionally used https://reqres.in for that delay
  const input = "https://reqres.in/api/users?delay=1";

  return wrapPromise(fetch(input).then(value => value.json()));
}

Then I used this component in order to render each user.

// UserListItem.js

import React from "react";

function UserListItem({ email, first_name, id, last_name }) {
  return (
    <li key={id}>
      <span>
        {first_name} {last_name}
      </span>
      <p>{email}</p>
    </li>
  );
}

export default UserListItem;

Then I simply wrapped my component that contains the data with Suspense.

// App.js
import React, { Suspense } from "react"; 

import UserList from "./UserList";

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <UserList />
    </Suspense>
  );
}

export default App;

And finally...

// UserList.js

import React from "react";

import { fetchUsers } from "./userApi";
import UserListItem from "./UserListItem";

const resource = fetchUsers();

function UserList() {
  const { data: users } = resource.read();

  return <ul>{users.map(UserListItem)}</ul>;
}

export default UserList;

Conclusion

My conclusion is that we went from a component that was:

  1. responsible for fetching the data
  2. fetching the data after rendering
  3. handling the UI while the data wasn't ready
  4. rendering the data when they were ready

to a component that is:

  1. rendering the data

You can find the complete example here.

Simple React Engineer


  1. There aren't any implementations to integrate with Suspense yet (except Relay I think). We have to copy paste from here 😂 write our own function at the moment. 

Top comments (3)

Collapse
 
thorocaine profile image
Jonathan Peel

This is interesting and I am going to try it, I just don't understand what is happening with that wrapper.
Where does the read method come from?
It's like you want the result of the parameter to the method.
(I will still give it a go. If it works, it works).

The next question I would ask would be if you need to pass a parameter to the API.
I guess you would just need to create a method to return this method (:P) and "read" it from a callback. (or do it the old way)

Collapse
 
potouridisio profile image
Ioannis Potouridis

I'll send you two examples from React that answer your questions.

You can see the implementation of that method here.

And here is how you pass a parameter.

Collapse
 
thorocaine profile image
Jonathan Peel

That is nicely put.

Until this is actually ready, I might stay away, but it makes me want to try and create a wrapper function for an API call that would return the result of a reducer (if that makes sense).
The loading state would still need to be managed.