DEV Community

Brian Neville-O'Neill
Brian Neville-O'Neill

Posted on • Originally published at blog.logrocket.com on

The future of React, unfolding with Suspense

Ever since the React team released their 16.x vision, it has definitely caught the community by storm. Some of the coolest additions to the collection are Hooks, lazy loading, Suspense, and the cache API.

This won’t be just another article on how to write Hooks, but rather what to expect in the future of React! If you have never heard of Hooks, or any other new API in React, this article will be a great start to get you excited about the future.

As we dive deeper in the article, we will cover two new concepts, which are expected to be released in Q2 2019:

  • How to use Suspense for fetching data
  • How to use react-cache

I am already excited! But let’s slow down and do a quick recap before we dive deeper.

LogRocket Free Trial Banner

React Hooks

With React 16.8, Hooks are officially a part of the stable release. Some of the problems it has solved, on a high level:

  • By adopting the concept of writing everything using functions, it has made writing code more modular and easier to maintain
  • Discouraging the use of HOCs and other complex functions that made code difficult to understand
  • Discarding the use of complex lifecycles like componentDidMount, componentDidUpdate, etc., which required us to write repetitive code

If you want to know more in detail, check here.

So, let’s check out a demo of React Hooks and how a typical app might look!

React.lazy

The name really gives away its intent! We need it when we want to lazily load components:

const _TodoList_ = _React.lazy_(() => import("./containers/todoList"));
Enter fullscreen mode Exit fullscreen mode

With the help of dynamic import using webpack, we could do it; it helps create bundles, which improves our page load speed. Let’s have fun with a demo! Just go back up to the Codesandbox demo link and change the imports to the following below:

const TodoList = React.lazy(() => import("./containers/todoList"));
const CompletedList = React.lazy(() => import("./containers/completedList"));
const AddNewTask = React.lazy(() => import("./containers/addNewTask"));
Enter fullscreen mode Exit fullscreen mode

Notice in the below image how separate bundles are created. 😄

Bundles Created With Webpack

Suspense

Suspense is fairly simple to use. Let’s better understand this with the help of a code demo:

// https://codesandbox.io/s/new-6m2gj
import React, { useState, useEffect, Suspense } from "react";
import ReactDOM from "react-dom";
import todoListData from "./containers/todoList/todolistData";
import Header from "./containers/header";
import Clock from "./components/Clock";
import "./styles.css";

const TodoList = React.lazy(() => import("./containers/todoList"));
const CompletedList = React.lazy(() => import("./containers/completedList"));
const AddNewTask = React.lazy(() => import("./containers/addNewTask"));

function App() {
  const { todolist } = todoListData;
  const [todoListPayload, setTodoListPayload] = useState(todolist);
  const [completedTodoList, setCompletedTodoList] = useState([]);

  const addTodoTaskHandler = value => {
    // addTodoTaskHandler
  };

  const removeTodoTaskHandler = ({ id }) => {
    // Remove from the set of todo list
  };

  const completeTodoTaskHandler = ({ id }) => {
    // Get item to remove
  };

return (
    <div className="App">
      <Header title={"My Tasks"} />
      <Clock />
      <div className="PageLayout">
        <Suspense fallback={<div>Loading...</div>}>
          <TodoList
            payload={todoListPayload}
            completeTodoTaskHandler={completeTodoTaskHandler}
          />
          <CompletedList list={completedTodoList} />
          <AddNewTask addTodoTaskHandler={addTodoTaskHandler} />
        </Suspense>
      </div>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Enter fullscreen mode Exit fullscreen mode

You can find the demo link here, in case you want to play with it.

If we check the code in the demo, we’ll see:

<Suspense fallback={<div>Loading...</div>}>
  <TodoList
     payload={todoListPayload}
     completeTodoTaskHandler={completeTodoTaskHandler}
   />
  <CompletedList list={completedTodoList} />
  <AddNewTask addTodoTaskHandler={addTodoTaskHandler} />
</Suspense>
Enter fullscreen mode Exit fullscreen mode

It is as simple as wrapping the components with Suspense. We lazily loaded some of the components — TodoList, CompletedList, AddNewTask — using React.lazy() . Since internally it will generate bundles for each, in slower network conditions, it might take some time to load them.

Suspense will automatically take care of that by showing a fallback such as Loading… or any other component, such as a spinner or similar.

Diving deeper into the future

Great! Our short recap was intense. Now let’s have some more fun with Suspense.

Suspense and react-cache

Wait, haven’t we covered Suspense? Well, what if I told you Suspense can also handle our loading state when an API is called? But for that, we really need to dig into the API and understand it better.

After some digging and research, I finally found Shawn Swyx Wang’s 🌟 GitHub repository, and I would like to quote directly from his doc:

React Suspense is a generic way for components to suspend rendering while they load data from a cache.

Problems it solves: When rendering is I/O-bound.

OK, “load data from a cache” gave me a hint, but I needed more information on how I can really handle the API.

Kent C. Dodds taught an important concept in his Egghead lesson: Suspense automatically knows that an API request has been called if we throw a promise.

import React, { Suspense } from "react";

fetchArticles() {
  // Some fetch API fetching articles
}

let isRequestCalled = false;
function Content() {
  let result = [];
  if (!cache) {
    const promise = fetchArticles();
    isRequestCalled = true;
    throw promise; // Let suspense know
  }
  return <div>Article</div>;
}

const Articles = () => {
  return (
    <div>
     {/* Yay promise is thrown */}
      <Suspense fallback={<div>loading...</div>}>
        <Content />
      </Suspense>
    </div>
  );
};

export default Articles;
Enter fullscreen mode Exit fullscreen mode

Of course, this isn’t the best way to handle code; it looks kinda hacky. So let’s try to use react-cache to handle this code better:

import React, { Suspense } from "react";

import { unstable_createResource as createResource } from "react-cache";

function fetchArticles() {
  // Some fetch API fetching articles
}

const politicalArticles = createResource(fetchArticles);

function Content() {
  const result = politicalArticles.read(someKey);
  return <div>Article</div>;
}

const Articles = () => {
  return (
    <div>
      <Suspense fallback={<div>loading...</div>}>
        <Content />
      </Suspense>
    </div>
  );
};

export default Articles;
Enter fullscreen mode Exit fullscreen mode

createResource from react-cache creates a resource out of a callback, returning a promise.

Well, for Suspense to know that it has to show loading state, all it needs is a promise. It will continue to show the loading state until the promise is resolved.

However, this is experimental. I am sure you will run across errors, so don’t worry, it is clearly mentioned that react-cache is still under development.

Just a heads-up, make sure you use the read method inside a component; otherwise, it will throw an error.

// A snippet from the React-cache library

function readContext(Context, observedBits) {
  const dispatcher = ReactCurrentDispatcher.current;
  if (dispatcher === null) {
    throw new Error(
      'react-cache: read and preload may only be called from within a ' +
        "component's render. They are not supported in event handlers or " +
        'lifecycle methods.',
    );
  }
  return dispatcher.readContext(Context, observedBits);
}
Enter fullscreen mode Exit fullscreen mode

In case you are interested in reading the react-cache source code, check this link.

Congratulations!

We are now caught up on the near future of React, and one thing is evident: the React team wants to make the API as simple as possible.

I am also excited that more and more libraries are moving towards functional programming. This pattern will definitely revolutionize the way we write frontend. I am watching out for concurrent React, too — in case you are interested, check out the official roadmap docs. React-cache and Suspense are some of the features that are part of concurrent react. 😎

Follow me on Twitter to get updates regarding new articles and the latest frontend developments. Also, share this article on Twitter to help others find it. Sharing is caring.


Plug: LogRocket, a DVR for web apps

LogRocket Dashboard Free Trial Banner

LogRocket is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

Try it for free.


The post The future of React, unfolding with Suspense appeared first on LogRocket Blog.

Top comments (0)