World of React in 2021

Ravi Sanapala
Webtips
6 min readSep 7, 2021

--

What’s the best way to build React apps in 2021? What has changed since 2016? What libraries is everyone using these days?

I started using React in 2016. I Still remember the initial days when I was working for a startup in T-Hub Hyderabad, The discussion was whether to go with Angular / ReactJS to develop the application.

Over the last few years, there have been some big changes in React itself, and the ecosystem has evolved quite a bit too.

Thinking in React: Thinking the Same way

The core skill of “thinking in React” hasn’t changed much at all over the years. It’s still all about one-way data flow, props, state, and JSX. Things like being able to break a design into components are still crucial, as is deciding which components should “own” data and which should merely display it.

Hooks vs Classes: Now we prefer Hooks

In recent years, the biggest shift in React is from classes to hooks. Hooks were added in React 16.8 (Feb 2019), and have pretty quickly become the standard way that people write React components.

Initially, hooks look weird, especially if you’ve been programming a while. Variables that seemingly maintain state between function calls seem pretty magical. It’s more about arrays than magic, though.

Once you settle in with how hooks work and get a feel for useState, the next big hurdle to overcome is the useEffect hook.

useEffect is the answer to how to do lifecycle methods in function components. Except it doesn’t really work the way lifecycles do, at all. It’s crucial to grasp the mental model of the useEffect. Once you have that down, solving problems will get easier and easier.

The Best React Libraries in 2021

On the library front, community favorites have changed over the years, and continue to evolve.

Routing in the React Application

React Router is still the dominant router (and despite the name, not actually part of React itself). React Router is a standard library for routing in React. It enables the navigation among views of various components in a React Application, allows changing the browser URL, and keeps the UI in sync with the URL.

State Management in the React Application

Redux is still used in a lot of apps, hovering around 30–50% last I saw. The new official Redux Toolkit is excellent, too. It helps cut down boilerplate quite a bit, in combo with Redux’s hook. If you are going to use Redux, be sure to check those out.

Redux is less of the de-facto standard than it once was, though. More people are realising that React’s built-in state management is enough for a lot of use cases, especially for simple ones.

There are also some newer purpose-built libraries for things that you might’ve used Redux for before. I’ll mention a couple below.

MobX is probably the most popular alternative to Redux outside of the built-in Context API. Where Redux is all about being explicit and functional, MobX takes the opposite approach. It uses ES6 Proxies behind the scenes to detect changes, so updating observable data is as easy as using the plain old = assignment operator.

Context API

If your global state consists of a couple of things that rarely change (current user, current theme, current language, etc), you don’t need a library to pass that stuff around.

The Context API + useContext are good for passing around a simple global state managed by useReducer.

The Context API was re-done in React 16.3. The old contextType thing is out, and the old guidance about avoiding Context unless you’re a library maintainer has been gone for a while. The useContext hook makes it really nice to use.

Data Fetching

On the data-fetching front, the strategy of putting everything in Redux or a global store is getting less common.

react-query does a good job at fetching data and managing loading/success/error states. It takes care of maintaining that global data cache across component boundaries without you really having to think about it.

React-query

It’s less about the specific library and more about the pattern. (swr is another good option)

Take a common scenario like a ListPage / DetailPage for a set of items. You open the ListPage, it fetches all the widgets or whatever. Good so far.

Normally you’d probably keep that data around in Redux or something so that when you click into one of the DetailPages, that item has probably already been loaded.

And then the user clicks Back, and they’re back at ListPage again, but you already have the data so you can just display it.

It all works fine, but there are edge cases. What if an item went stale between the time the user loads ListPage and they click into a DetailPage? What if, while they were perusing the DetailPage, some new items were added to the list?

When should you re-fetch that data? And how do you deal with merging those two kinds of things — a list response that could maybe replace the whole list, and a single item response that should replace only one item? In Redux, the reducer handles that, but you’ve gotta write that stuff manually for the most part.

It all gets even more complex once you start thinking about pagination, and whether you want to cache pages, or refetch all the pages, or whatever.

All of this stuff, I think, falls under the “client data management” umbrella, and we’ve been using generic state management libraries for that for a long time now. And we have to solve these problems over and over again, or we ignore them and hope they don’t happen or patch them as they crop up.

Libraries like react-query slice the problem up differently.

It knows you’re going to fetch data, and it knows you’re going to want to cache that data globally under some key (maybe items or a nested items[id]). It also knows that you’re going to want to update that data sometimes – based on a timer, or when the user tabs away from the app and back again, etc.

Because this stuff is stored in a globally accessible cache, every component that needs access can call useQuery('items', fetchItems) to grab that data, and it’ll automatically be fetched if it’s not already available. And it deals with idle/loading/error/success states too.

It takes any Promise-returning function, so it works with fetch or axios or whatever data fetcher you want to use.

This is what I meant when I said that I think it gets the abstraction right — we can use whatever we were already using to make the HTTP call, but react-query steps in to handle the oft-repeated heavy lifting that’s common to most data fetching use cases.

State Machines are Trending

XState is a library for building state machines, which are excellent for representing complex logic. Actually, they’re pretty great for not-so-complex logic, too. Next time you find yourself juggling a bunch of boolean values or trying to update a bunch of variables in the right places, check out XState. egghead.io has a nice course on XState by Kyle Shevlin.

There’s an alternative called Robot and I wrote a tutorial using it to build a confirmation modal flow if you want to get a feel for how state machines can be useful.

Bundlers

Webpack is still everywhere. It’s up to version 5 now. The config syntax changed a lot somewhere around v2 or v3.

Most people use Create React App to start new apps nowadays, which is great, and shields you from Webpack unless you really need to customize it. The defaults are pretty solid. If you need to customize things, check out craco.

Forms

Formik and react-hook-form seem to be the favorites right now, with hook-form gaining steam.

Suspense

React’s long-awaited Suspense feature is… still awaited. It’s in React right now, and you can try it out, but it’s in experimental mode and it’s not advisable to build production code with it. The API can still change.

Server Components

The latest advancement is components that render on the server, coupled with a server-side framework around React. These are still experimental too. Very cool though, and I suspect will change the ecosystem quite a bit. Check out the official announcement and demo video from the React team to learn more.

--

--