9 New React and JavaScript Links for February 2022

Every now and then, I find that I’ve accumulated a bunch of links about various things I find interesting. Like React and JavaScript! Here’s a list of nine links to other articles about them that I’ve been saving up and think are worth sharing.

React and JavaScript code snippets with colorful hand-marked scribbles of notes.
Source: “Good advice on JSX conditionals” by Vladimir Klepov
  • Seed Funding for Remix
    Remix went open source after taking funding which seems like a solid move. It’s a for-now-React-only framework, so I think it’s fair that everyone asks how does it compare to Next.js. Which they answered. Probably worth noting again for us CSS folks, Kent mentioned: “Because Remix allows me to easily control which of my CSS files is on the page at any given time, I don’t have all the problems that triggered the JavaScript community to invent workarounds like CSS-in-JS.”
  • React Router v6
    Speaking of that gang, they released React Router v6, which looks like a positive move — all hooks based, 50% smaller than v5 — but is yet another major version with API changes. React Router has a history of API changes like this and they trigger plenty of grumbling in the community. There is plenty of that again.
  • React Aria
    “A library of React Hooks that provides accessible UI primitives for your design system” from… Adobe. Interesting. Looks like some pretty hard problems being solved here, like FocusScope (“When the contain prop is set, focus is contained within the scope.”) and interesting color inputs, like useColorField, useColorSlider, and useColorWheel. There are 59 hooks in all, ranging from interactions and forms to overlays and internationalization, with plenty of others in between.
  • Front End Tables: Sorting, Filtering, and Pagination
    Tania Rascia: “One thing I’ve had to do at every job I’ve had is implement a table on the front end of an application that has sorting, filtering, and pagination.” No shame in reaching for a big library with all these features, but sometimes it’s best to DIY.
  • Good advice on JSX conditionals
    Vladimir Klepov covers the (weirdly) many ways fairly simple conditionals can go wrong, like the number 0 leaking into your markup, and how to manage update versus remount in conditionals.
  • useProseMirror
    I’ve found ProseMirror to be a pretty nice rich text editor in the past. The library itself isn’t actually in React, so I think it’s a smart call here to make a modern React wrapper for it.
  • Spead up sluggish inputs with useDeferredValue
    You can introduce gnarly input delay the more work that an onChange function has to do on a text input. useDeferredValue gives us a way to separate high priority updates from low priority updates for cases like this.”
  • 🎥 A Cartoon Intro to WebAssembly
    If you don’t have a good understanding of what WebAssembly is, then Lin Clark will get you there in this video from JSConf EU 2017. So, no, not a new link or anything, but it’s new to me!
  • 🎥 Turborepo Demo and Walkthrough
    Vercel bought Turborepo. Turborepo is specifically focused on making monorepos better. As someone who’s main codebase is a monorepo with Lerna and Yarn Workspaces such that we can have multiple different sites all share things like a design system, this is right up our alley. This video is with the Turborepo creator Jared Palmer and Lee Robinson, head of developer relations at Vercel. In this video, you get to see it all work.


9 New React and JavaScript Links for February 2022 originally published on CSS-Tricks. You should get the newsletter.

Improving Your Team’s Communication In The Age Of Remote Work

Products are not built in isolation. A big part of our professional lives is spent discussing, brainstorming, and deciding alongside others. No matter our field of expertise we need our team’s knowledge to amplify our own.

With the rise of remote work, we’re communicating more and more in written instead of spoken form and teams need to adapt. If communication fails, everything else fails too.

Why Is In-Person Communication So Effective But Also So Messy?

When we talk with others in person we receive a lot of information. We can read the room to acknowledge unspoken agreements, alliances, tensions, and the overall mood of everyone, and react accordingly. We do this almost unconsciously, making face-to-face communication more effective.

However, the same reason also makes it messy. If someone is having a bad day, we might see it as a sign of tension or lack of investment in the project. For outsiders, lacking an understanding of the complex dynamics of a team can lead to the wrong conclusions.

A downside of office work is how easily information silos are created. Having colleagues next to us makes it easier to ask for help by tapping on their shoulder than to create documentation and rely on it. This creates information disparity, where a few individuals hold critical knowledge. Is everyone in your team relying on a single person’s knowledge? Or is knowledge safely stored in software instead?

I’ve been there before. I used to work on an over-engineered product where only one person fully understood it. We relied on him to get tricky tasks done. We were “too busy to document it” and instead spent the better part of a year slowly refactoring it. Instead, we should’ve taken the time to document how it worked and then done a workshop for everyone to catch up. It would’ve made us more productive, eased the refactoring and our time working with the product.

Changing Our Mindset: Remote Work Is Not An Online Office

Becoming a productive remote team requires a change of mindset: defaulting to asynchronous instead of synchronous collaboration. This allows people to focus on what matters by decreasing distractions and prevents a culture of “always online” which is an adaptation of tap-on-the-shoulder communication to chat and email.

Allow Yourself Some Deep Work

Research has shown that we need from 15 to 30 minutes of focusing on a task before we’re fully immersed in it and are able to do meaningful work. The worst part is that every time we’re interrupted we’ll need to start over. A tap on the shoulder, call, or notification can break our focus. Being “in the zone” makes us more productive by letting our minds solve one hard problem at a time, instead of multitasking, which we’re terrible at. This has been dubbed “Deep Work”.

Achieving deep work should be our goal in any team, but doing it in an office setting can be challenging because of so many distractions. Asynchronous communication in a remote setting is perfect for it.

Once you’re not required to be always-online, the opportunity to do meaningful work will drastically increase, but your ability to get help will get slower, so how can we get the best of both worlds?

Documentation As A First-Class Citizen

Working groups form a network of knowledge, where each individual will have unique information that is accessible by everyone. As we share information among us, we form an institutional memory that makes the team more productive, but can also hinder us.

If you find a solution to a hard problem, others can ask for your help when facing it. But what happens if someone needs your knowledge in two weeks when you’re on holiday? The solution is to offload it onto software that is always accessible, and to foster an environment where everyone sees the value of documentation as a first-class citizen. However, it’s easier said than done.

Asking someone at the office to create documentation, although logical, might be ignored since everyone is nearby and ready to help. But remote teams appreciate being self-reliant when facing issues.

The bigger the team, the harder communication gets, and having a central repository of knowledge helps to tame complexity. By making documentation the default instead of an afterthought, you’re improving your institutional memory, which makes critical information accessible by anyone, anytime, anywhere.

What should you document? Solutions to thorny problems, outages and their causes, onboarding, new tech or tools introduced, fruitful and unfruitful discoveries. It’s all valuable information that should be accessible.

For example, when I joined a remote-first company my manager assigned me a Notion document with goals for the first two weeks. The first week was about reading our “Engineering Documentation”, which explains most of our tech stack and how to set it up locally. The second week I had to do my first task, which also had everything documented: requirements, deliverables, stakeholders. It was by far the best onboarding I’ve had.

Defaulting To Public Communication

Private communication hurts remote teams. Because of it, knowledge and information are not shared equally, and no matter how trivial a discussion is, others might benefit from it. For your remote team to communicate effectively, doing it in public is an important step.

Keeping communication private is similar to not having documentation. You’re storing information in your head and nowhere else. This fosters information silos, decreasing productivity as a result.

Some topics will be sensitive enough to warrant private communication though, such as health or workplace issues, but if you’re collaborating privately consider your motivations and try to switch to public instead.

Public communication in an office is difficult because you can’t interrupt everyone all the time to share something, and it’s a good reason why agile processes such as daily stand-ups exist. But with asynchronous collaboration, everyone will read through the announcements in the team channel whenever they can. Daily standups can either be ditched or become a time to socialize instead.

In companies where communication happens in private, changing to a public mindset can be challenging, but the benefits are worth it. Some tips to facilitate the move is to have clearly defined channels for different topics in your chat app. That way everyone knows where to ask questions and have discussions. Making everyone aware that the team should communicate in public is important too.

But beware, too many channels (private or public) can hurt productivity since everyone spends more time deciding where to communicate rather than how. Only have as many channels as you really need, only invite the necessary people to them, and understand that it’s OK to mute unimportant channels.

When everything happens in public your team is more interconnected and it facilitates leading by example since everyone rises to the challenge of being better.

The Rules For Effective Communication

Expressing one’s ideas effectively is hard, but by following some rules we can dramatically improve how useful each message we send is.

Every Message Is Actionable, Asks A Question, Or Informs, And Has Context

From the perennial “let’s go for a tea/coffee” to the famous water cooler brainstorming session, in-person communication is riddled with traditions. We use these traditions to simplify complex collaboration.

We’re still figuring out the best habits to help us communicate online, but some great rules to follow is to make every message we send actionable, a question, or informative, and to include its context.

Now, let’s break down the rules.

Actionable Messages

With actionable messages, there’s something to be done. A ticket can be created, a solution provided, or a discussion had.

Compare a non-actionable message with little context:

“I clicked the subscribe button and a modal showed up.”

With an actionable one:

“On the products page, I clicked the ‘Subscribe’ button and a modal opened. But I should’ve been redirected to the subscribe page instead. Are we aware of it?”

In this case, either the behavior is wrong, or the specification is out of date and there’s a clear action: A ticket can be created or the documentation updated.

Your Message Asks A Question

A good question explains the why behind the question and provides context. The trick is to maximize the chances of someone understanding your problem right away by explaining it as clearly as possible. If you’re asking for help with a problem, highlight what you’ve tried so far and If you haven’t tried to solve it yet, try first.

Compare an unclear question with no context, reason, or attempts:

“Does anyone know how to render a modal with React Router?”

With a clear one:

“I need to show a modal with our newsletter when a button is clicked, but I’m not managing to do it with React Router. I tried using a <Link to="/modal" /> but it redirects to a page instead of opening the modal in-place. Can someone help?”
Informative Messages

A good informative message is self-contained. You don’t need a lot of prior knowledge to understand it and its value.

Here’s an informative but unclear message:

“We reached an all-time high of users today!”

And here’s a self-contained one that has context:

“We reached 10k concurrent users today on the site, an all-time high! That’s a 25% increase from last month.”
Add Context To Your Message

If you noticed, all rules mentioned context. Adding a message’s context has an anchoring effect, helping the reader better understand the rest of the information.

Here are the same messages as above but with context struckthrough. Notice how much better they are if they contain it:

“On the products page, I clicked the ‘Subscribe’ button, and a modal opened. But I should’ve been redirected to the subscribe page instead. Are we aware of it?”
“I need to show a modal with our newsletter when a button is clicked, but I’m not managing to do it with React Router. I tried using a <Link to="/modal" /> but it redirects to a page instead of opening the modal in-place. Can someone help?”
“We reached 10k concurrent users today on the site, an all-time high! That’s a 25% increase from last month”.

Chat, Documentation, Video, Product Management: Where Does It All Fit?

Would you send an email to a colleague sitting next to you, asking if they’d like to go for a coffee? Probably not. You can be more productive by using your tools as they were intended to be used, and a remote team needs many of them to collaborate effectively. Let’s see how to best use each type.

Media: An Image Is Worth A Thousand Words

Adding an image to a bug report, a video to a feature announcement, or an emoji to a celebratory message can make all the difference between understanding and confusion. Using media helps to explain visually what is hard to describe.

There are great tools for creating editable screenshots or videos to enhance your messages: CloudApp, Gyazo, and the default OS screenshot app are some options I’ve been happy with.

Many services allow you to use emojis for collaboration such as Jira or GitHub. For example, a thumbs up 👍 usually means “good” or “acknowledged“ and is widely understood, but make sure your team is aligned on what less common emojis mean. In our team, we use the tulip emoji 🌷 in code reviews to express an “Improvement, but not required” comment. We have that usage documented under “how to do code reviews”.

Chat: Short Form Discussion, Quick Decisions, Questions, Announcements

Chat tends to be the beating heart of a team. It’s a newspaper of the current events, where most collaboration happens.

To improve chat communication, create laser-focused channels. Each project should have a channel so that any conversation about it can be kept in one place. Just beware not to overdo it and harm productivity as a result.

Chat is also a great place to talk, make jokes, and connect with your peers. Create “share” channels where random discussions, articles, and funny cat gifs don’t interrupt serious conversations.

Depending on the app you use, you might have “threads”. If you do, try to keep all discussion inside a thread. It dramatically reduces noise in the main channel and keeps the conversation focused.

Documentation: Long-Form Discussion, Asynchronous Brainstorming, Evergreen Information

Deciding whether to document something can be tricky. If the answer to any of the following questions is yes then you should document it.

  • Will someone need to read this again?
  • Will we keep contributing to this?
  • Is this a repeating event?
  • Is this information evergreen?

Event summaries, technical guides, team decisions or guidelines, checklists, and results of brainstorming sessions should all be documented. Writing documentation isn’t fun, but spending two hours writing something that could save hours for multiple people in the long run is a huge productivity win. Time savings compound!

Documentation saves time in other ways too. It helps to spread knowledge throughout the organization, to answer questions, and people to be self-reliant. You can create templates for common tasks such as team meetings and investigation tasks to simplify the work. Apps like Notion and Confluence have ready-made templates you can choose from or you can create your own.

When chat discussions are becoming a long thread, move it over to documentation. One person writes down a summary of the problem at hand, and everyone can collaborate asynchronously.

For more effective decision making through documentation, explain the problem at hand, its context, and list possible solutions. That helps stakeholders choose and leads to more focused discussions, instead of a back and forth between options.

Product Management: Laser-Focused Documentation

Product management tools such as Jira are complex. But they can boost productivity by connecting any decision or important information relevant to a task in a corresponding ticket. This is a game-changer if done properly.

This follows the idea of having a single source of truth. By compiling all the information about a feature or task in a single place it becomes simple to stay up to date and to solve misunderstandings by pointing to earlier agreements. If the feature is blocked, put on pause, or someone else takes over, being able to follow through with all the decisions saves a lot of time, and anyone that needs to verify information knows where to find it.

We often make product decisions in chat or documentation. Keep your ticket up to date by adding a link to it. For example:

“We’ll implement animations next sprint to ship on time, discussion in the link below.”

However, we’re all human. We’ll miss things. Decisions won’t be recorded, information might not be up to date, and some knowledge will live in our heads, but porting at least some information could save someone hours down the road.

Video: Sometimes There’s Nothing Like Face-To-Face

We should try to offload knowledge and information onto software tools for organization and ease of access, but sometimes talking to each other directly is priceless. Being able to call our colleagues friends is a valuable part of our professional lives, but creating strong connections in a remote team is challenging. Talking directly to others helps a lot.

From pair-programming, brainstorming in real-time, important private conversations, delivering critical news, or simply hanging out, video conferences excel at making us feel connected to our peers.

Team rituals are great ways to strengthen bonds and to foster human connection. You’ll be surprised at how natural it becomes to talk with friends via video after a while. In my team, we meet every Tuesday to do a roundup of what everyone’s working on by demoing it and to crack a few jokes in between. On Fridays, we play simple browser games together and have a good time.

But beware, overuse of video calls can hurt productivity just as much as an open office. A request for help via video is hard to deny. Make sure your team is respectful of each other’s needs to focus. Having an asynchronous mindset helps. If video is needed, schedule it, it could be in 30 minutes or tomorrow morning. Unless it’s something critical everyone is happy to wait to get help.

Video communication should be linked with your other tools too. If a decision is made in a call you should document it, it’s easy to forget what was agreed on otherwise. If the meeting is important consider recording it.

Just like any other productivity tool, you can use video to save time. Switch to it when the conversation is becoming entangled in confusion, there’s too much context to share via text, or an in-person explanation is better.

Allow Yourself And Your Team To Be Productive

Follow all the best practices highlighted in this article and you’ll notice how you and your team spend less time on discussions, disagreements, and reminding each other about things, and more time-solving problems and doing the things you enjoy.

Getting started can feel overwhelming, so the first step is to promote asynchronous communication by letting others know that answering chat, contributing to documentation, and even video calls should be done when it’s not interrupting their focus time.

Start leading by example. Don’t wait for others to improve the docs or send well-crafted messages. Soon enough, everyone in your team will consider improving communication as a top priority and will start to contribute ideas and find ways to be more productive.

Further Reading

Collective #628



Collective 628 item image

This content is sponsored via Thought Leaders

Cut Your IT Bills in Half

Deploy more with Linux virtual machines, global infrastructure, and simple pricing. No surprise invoices, no lock-in, and the same price across 11 global data centers. Industry-leading price-performance. Shared, Dedicated, High Memory, and GPU instances plus S3-Compatible Object Storage, Managed Kubernetes, and more. $100 in cloud infrastructure credit.

Try Linode Free


Collective 628 item image

Faster Web App Delivery with PRPL

Learn all about PRPL, a pattern for structuring and serving web applications and Progressive Web Apps (PWAs) with an emphasis on improved app delivery and launch performance.

Read it









Collective 628 item image

Webpack 5 Headache

Sindre Sorhus on how Webpack no longer automatically polyfilling Node.js APIs is a huge breaking change and will inconvenience both users and package maintainers.

Read it


Collective 628 item image

Announcing Ionic Vue

Read about the release of Ionic Vue, a native Vue version of Ionic Framework that makes it easy to build apps for iOS, Android, and the web as a Progressive Web App.

Read it





Collective 628 item image

Tailwind CSS tutorial

A Tailwind CSS tutorial that covers the installation via a package manager, generating the configuration file, building a website and reducing the final CSS file.

Read it



The post Collective #628 appeared first on Codrops.

React Suspense in Practice

This post is about understanding how Suspense works, what it does, and seeing how it can integrate into a real web app. We'll look at how to integrate routing and data loading with Suspense in React. For routing, I'll be using vanilla JavaScript, and I'll be using my own micro-graphql-react GraphQL library for data.

If you're wondering about React Router, it seems great, but I've never had the chance to use it. My own side project has a simple enough routing story that I always just did it by hand. Besides, using vanilla JavaScript will give us a better look at how Suspense works.

A little background

Let’s talk about Suspense itself. Kingsley Silas provides a thorough overview of it, but the first thing to note is that it's still an experimental API. That means — and React’s docs say the same — not to lean on it yet for production-ready work. There’s always a chance it will change between now and when it’s fully complete, so please bear that in mind.

That said, Suspense is all about maintaining a consistent UI in the face of asynchronous dependencies, such as lazily loaded React components, GraphQL data, etc. Suspense provides low-level API's that allow you to easily maintain your UI while your app is managing these things.

But what does "consistent" mean in this case? It means not rendering a UI that's partially complete. It means, if there are three data sources on the page, and one of them has completed, we don't want to render that updated piece of state, with a spinner next to the now-outdated other two pieces of state.

What we do want to do is indicate to the user that data are loading, while continuing to show either the old UI, or an alternative UI which indicates we're waiting on data; Suspense supports either, which I'll get into.

What exactly Suspense does

This is all less complicated than it may seem. Traditionally in React, you'd set state, and your UI would update. Life was simple. But it also led to the sorts of inconsistencies described above. What Suspense adds is the ability to have a component notify React at render time that it's waiting for asynchronous data; this is called suspending, and it can happen anywhere in a component's tree, as many times as needed, until the tree is ready. When a component suspends, React will decline to render the pending state update until all suspended dependencies have been satisfied.

So what happens when a component suspends? React will look up the tree, find the first <Suspense> component, and render its fallback. I'll be providing plenty of examples, but for now, know that you can provide this:

<Suspense fallback={<Loading />}>

…and the <Loading /> component will render if any child components of <Suspense> are suspended.

But what if we already have a valid, consistent UI, and the user loads new data, causing a component to suspend? This would cause the entire existing UI to un-render, and the fallback to show. That'd still be consistent, but hardly a good UX. We'd prefer the old UI stay on the screen while the new data are loading.

To support this, React provides a second API, useTransition, which effectively makes a state change in memory. In other words, it allows you to set state in memory while keeping your existing UI on screen; React will literally keep a second copy of your component tree rendered in memory, and set state on that tree. Components may suspend, but only in memory, so your existing UI will continue to show on the screen. When the state change is complete, and all suspensions have resolved, the in-memory state change will render onto the screen. Obviously you want to provide feedback to your user while this is happening, so useTransition provides a pending boolean, which you can use to display some sort of inline "loading" notification while suspensions are being resolved in memory.

When you think about it, you probably don't want your existing UI to show indefinitely while your loading is pending. If the user tries to do something, and a long period of time elapses before it's finished, you should probably consider the existing UI outdated and invalid. At this point, you probably will want your component tree to suspend, and your <Suspense> fallback to display.

To accomplish this, useTransition takes a timeoutMs value. This indicates the amount of time you're willing to let the in-memory state change run, before you suspend.

const Component = props => {
  const [startTransition, isPending] = useTransition({ timeoutMs: 3000 });
  // .....
};

Here, startTransition is a function. When you want to run a state change "in memory," you call startTransition, and pass a lambda expression that does your state change.

startTransition(() => {
  dispatch({ type: LOAD_DATA_OR_SOMETHING, value: 42 });
})

You can call startTransition wherever you want. You can pass it to child components, etc. When you call it, any state change you perform will happen in memory. If a suspension happens, isPending will become true, which you can use to display some sort of inline loading indicator.

That's it. That's what Suspense does.

The rest of this post will get into some actual code to leverage these features.

Example: Navigation

To tie navigation into Suspense, you'll be happy to know that React provides a primitive to do this: React.lazy. It's a function that takes a lambda expression that returns a Promise, which resolves to a React component. The result of this function call becomes your lazily loaded component. It sounds complicated, but it looks like this:

const SettingsComponent = lazy(() => import("./modules/settings/settings"));

SettingsComponent is now a React component that, when rendered (but not before), will call the function we passed in, which will call import() and load the JavaScript module located at ./modules/settings/settings.

The key piece is this: while that import() is in flight, the component rendering SettingsComponent will suspend. It seems we have all the pieces in hand, so let's put them together and build some Suspense-based navigation.

Navigation helpers

But first, for context, I'll briefly cover how navigation state is managed in this app, so the Suspense code will make more sense.

I'll be using my booklist app. It's just a side project of mine I mainly keep around to mess around with bleeding-edge web technology. It was written by me alone, so expect parts of it to be a bit unrefined (especially the design).

The app is small, with about eight different modules a user can browse to, without any deeper navigation. Any search state a module might use is stored in the URL’s query string. With this in mind, there are a few methods which scrape the current module name, and search state from the URL. This code uses the query-string and history packages from npm, and looks somewhat like this (some details have been removed for simplicity, like authentication).

import createHistory from "history/createBrowserHistory";
import queryString from "query-string";
export const history = createHistory();
export function getCurrentUrlState() {
  let location = history.location;
  let parsed = queryString.parse(location.search);
  return {
    pathname: location.pathname,
    searchState: parsed
  };
}
export function getCurrentModuleFromUrl() {
  let location = history.location;
  return location.pathname.replace(/\//g, "").toLowerCase();
}

I have an appSettings reducer that holds the current module and searchState values for the app, and uses these methods to sync with the URL when needed.

The pieces of a Suspense-based navigation

Let's get started with some Suspense work. First, let's create the lazy-loaded components for our modules.

const ActivateComponent = lazy(() => import("./modules/activate/activate"));
const AuthenticateComponent = lazy(() =>
  import("./modules/authenticate/authenticate")
);
const BooksComponent = lazy(() => import("./modules/books/books"));
const HomeComponent = lazy(() => import("./modules/home/home"));
const ScanComponent = lazy(() => import("./modules/scan/scan"));
const SubjectsComponent = lazy(() => import("./modules/subjects/subjects"));
const SettingsComponent = lazy(() => import("./modules/settings/settings"));
const AdminComponent = lazy(() => import("./modules/admin/admin"));

Now we need a method that chooses the right component based on the current module. If we were using React Router, we'd have some nice <Route /> components. Since we're rolling this manually, a switch will do.

export const getModuleComponent = moduleToLoad => {
  if (moduleToLoad == null) {
    return null;
  }
  switch (moduleToLoad.toLowerCase()) {
    case "activate":
      return ActivateComponent;
    case "authenticate":
      return AuthenticateComponent;
    case "books":
      return BooksComponent;
    case "home":
      return HomeComponent;
    case "scan":
      return ScanComponent;
    case "subjects":
      return SubjectsComponent;
    case "settings":
      return SettingsComponent;
    case "admin":
      return AdminComponent;
  }
  
  return HomeComponent;
};

The whole thing put together

With all the boring setup out of the way, let's see what the entire app root looks like. There's a lot of code here, but I promise, relatively few of these lines pertain to Suspense, and I'll cover all of it.

const App = () => {
  const [startTransitionNewModule, isNewModulePending] = useTransition({
    timeoutMs: 3000
  });
  const [startTransitionModuleUpdate, moduleUpdatePending] = useTransition({
    timeoutMs: 3000
  });
  let appStatePacket = useAppState();
  let [appState, _, dispatch] = appStatePacket;
  let Component = getModuleComponent(appState.module);
  useEffect(() => {
    startTransitionNewModule(() => {
      dispatch({ type: URL_SYNC });
    });
  }, []);
  useEffect(() => {
    return history.listen(location => {
      if (appState.module != getCurrentModuleFromUrl()) {
        startTransitionNewModule(() => {
          dispatch({ type: URL_SYNC });
        });
      } else {
        startTransitionModuleUpdate(() => {
          dispatch({ type: URL_SYNC });
        });
      }
    });
  }, [appState.module]);
  return (
    <AppContext.Provider value={appStatePacket}>
      <ModuleUpdateContext.Provider value={moduleUpdatePending}>
        <div>
          <MainNavigationBar />
          {isNewModulePending ? <Loading /> : null}
          <Suspense fallback={<LongLoading />}>
            <div id="main-content" style={{ flex: 1, overflowY: "auto" }}>
              {Component ? <Component updating={moduleUpdatePending} /> : null}
            </div>
          </Suspense>
        </div>
      </ModuleUpdateContext.Provider>
    </AppContext.Provider>
  );
};

First, we have two different calls to useTransition. We'll use one for routing to a new module, and the other for updating search state for the current module. Why the difference? Well, when a module's search state is updating, that module will likely want to display an inline loading indicator. That updating state is held by the moduleUpdatePending variable, which you'll see I put on context for the active module to grab, and use as needed:

<div>
  <MainNavigationBar />
  {isNewModulePending ? <Loading /> : null}
  <Suspense fallback={<LongLoading />}>
    <div id="main-content" style={{ flex: 1, overflowY: "auto" }}>
      {Component ? <Component updating={moduleUpdatePending} /> : null} // highlight
    </div>
  </Suspense>
</div>

The appStatePacket is the result of the app state reducer I discussed above (but did not show). It contains various pieces of application state which rarely change (color theme, offline status, current module, etc).

let appStatePacket = useAppState();

A little later, I grab whichever component happens to be active, based on the current module name. Initially this will be null.

let Component = getModuleComponent(appState.module);

The first call to useEffect will tell our appSettings reducer to sync with the URL at startup.

useEffect(() => {
  startTransitionNewModule(() => {
    dispatch({ type: URL_SYNC });
  });
}, []);

Since this is the initial module the web app navigates to, I wrap it in startTransitionNewModule to indicate that a fresh module is loading. While it might be tempting to have the appSettings reducer have the initial module name as its initial state, doing this prevents us from calling our startTransitionNewModule callback, which means our Suspense boundary would render the fallback immediately, instead of after the timeout.

The next call to useEffect sets up a history subscription. No matter what, when the url changes we tell our app settings to sync against the URL. The only difference is which startTransition that same call is wrapped in.

useEffect(() => {
  return history.listen(location => {
    if (appState.module != getCurrentModuleFromUrl()) {
      startTransitionNewModule(() => {
        dispatch({ type: URL_SYNC });
      });
    } else {
      startTransitionModuleUpdate(() => {
        dispatch({ type: URL_SYNC });
      });
    }
  });
}, [appState.module]);

If we're browsing to a new module, we call startTransitionNewModule. If we're loading a component that hasn't been loaded already, React.lazy will suspend, and the pending indicator visible only to the app's root will set, which will show a loading spinner at the top of the app while the lazy component is fetched and loaded. Because of how useTransition works, the current screen will continue to show for three seconds. If that time expires and the component is still not ready, our UI will suspend, and the fallback will render, which will show the <LongLoading /> component:

{isNewModulePending ? <Loading /> : null}
<Suspense fallback={<LongLoading />}>
  <div id="main-content" style={{ flex: 1, overflowY: "auto" }}>
    {Component ? <Component updating={moduleUpdatePending} /> : null}
  </div>
</Suspense>

If we're not changing modules, we call startTransitionModuleUpdate:

startTransitionModuleUpdate(() => {
  dispatch({ type: URL_SYNC });
});

If the update causes a suspension, the pending indicator we're putting on context will be triggered. The active component can detect that and show whatever inline loading indicator it wants. As before, if the suspension takes longer than three seconds, the same Suspense boundary from before will be triggered... unless, as we'll see later, there's a Suspense boundary lower in the tree.

One important thing to note is that these three-second timeouts apply not only to the component loading, but also being ready to display. If the component loads in two seconds, and, when rendering in memory (since we're inside of a startTransition call) suspends, the useTransition will continue to wait for up to one more second before Suspending.

In writing this blog post, I used Chrome's slow network modes to help force loading to be slow, to test my Suspense boundaries. The settings are in the Network tab of Chrome's dev tools.

Let's open our app to the settings module. This will be called:

dispatch({ type: URL_SYNC });

Our appSettings reducer will sync with the URL, then set module to "settings." This will happen inside of startTransitionNewModule so that, when the lazy-loaded component attempts to render, it'll suspend. Since we're inside startTransitionNewModule, the isNewModulePending will switch over to true, and the <Loading /> component will render.

If the component is still not ready to render within three seconds, the in-memory version of our component tree will switch over, suspend, and our Suspense boundary will render the <LongLoading /> component.
When it’s done, the settings module will show.

So what happens when we browse somewhere new? Basically the same thing as before, except this call:

dispatch({ type: URL_SYNC });

…will come from the second instance of useEffect. Let's browse to the books module and see what happens. First, the inline spinner shows as expected:

If the three-second timeout elapses, our Suspense boundary will render its fallback:
And, eventually, our books module loads:

Searching and updating

Let's stay within the books module, and update the URL search string to kick off a new search. Recall from before that we were detecting the same module in that second useEffect call and using a dedicated useTransition call for it. From there, we were putting the pending indicator on context for whichever module was active for us to grab and use.

Let's see some code to actually use that. There's not really much Suspense-related code here. I’m grabbing the value from context, and if true, rendering an inline spinner on top of my existing results. Recall that this happens when a useTransition call has begun, and the app is suspended in memory. While that’s happening, we continue to show the existing UI, but with this loading indicator.

const BookResults: SFC<{ books: any; uiView: any }> = ({ books, uiView }) => {
  const isUpdating = useContext(ModuleUpdateContext);
  return (
    <>
      {!books.length ? (
        <div
          className="alert alert-warning"
          style={{ marginTop: "20px", marginRight: "5px" }}
        >
          No books found
        </div>
      ) : null}
      {isUpdating ? <Loading /> : null}
      {uiView.isGridView ? (
        <GridView books={books} />
      ) : uiView.isBasicList ? (
        <BasicListView books={books} />
      ) : uiView.isCoversList ? (
        <CoversView books={books} />
      ) : null}
    </>
  );
};

Let's set a search term and see what happens. First, the inline spinner displays.

Then, if the useTransition timeout expires, we'll get the Suspense boundary's fallback. The books module defines its own Suspense boundary in order to provide a more fine-tuned loading indicator, which looks like this:

This is a key point. When making Suspense boundary fallbacks, try not to throw up any sort of spinner and "loading" message. That made sense for our top-level navigation because there's not much else to do. But when you're in a specific part of your application, try to make your fallback re-use many of the same components with some sort of loading indicator where the data would be — but with everything else disabled.

This is what the relevant components look like for my books module:

const RenderModule: SFC<{}> = ({}) => {
  const uiView = useBookSearchUiView();
  const [lastBookResults, setLastBookResults] = useState({
    totalPages: 0,
    resultsCount: 0
  });
  return (
    <div className="standard-module-container margin-bottom-lg">
      <Suspense fallback={<Fallback uiView={uiView} {...lastBookResults} />}>
        <MainContent uiView={uiView} setLastBookResults={setLastBookResults} />
      </Suspense>
    </div>
  );
};
const Fallback: SFC<{
  uiView: BookSearchUiView;
  totalPages: number;
  resultsCount: number;
}> = ({ uiView, totalPages, resultsCount }) => {
  return (
    <>
      <BooksMenuBarDisabled
        totalPages={totalPages}
        resultsCount={resultsCount}
      />
      {uiView.isGridView ? (
        <GridViewShell />
      ) : (
        <h1>
          Books are loading <i className="fas fa-cog fa-spin"></i>
        </h1>
      )}
    </>
  );
};

A quick note on consistency

Before we move on, I'd like to point out one thing from the earlier screenshots. Look at the inline spinner that displays while the search is pending, then look at the screen when that search suspended, and next, the finished results:

Notice how there's a "C++" label to the right of the search pane, with an option to remove it from the search query? Or rather, notice how that label is only on the second two screenshots? The moment the URL updates, the application state governing that label is updated; however, that state does not initially display. Initially, the state update suspends in memory (since we used useTransition), and the prior UI continues to show.

Then the fallback renders. The fallback renders a disabled version of that same search bar, which does show the current search state (by choice). We've now removed our prior UI (since by now it’s quite old, and stale) and are waiting on the search shown in the disabled menu bar.

This is the sort of consistency Suspense gives you, for free.

You can spend your time crafting nice application states, and React does the leg work of surmising whether things are ready, without you needing to juggle promises.

Nested Suspense boundaries

Let's suppose our top-level navigation takes a while to load our books component to the extent that our “Still loading, sorry” spinner from the Suspense boundary renders. From there, the books component loads and the new Suspense boundary inside the books component renders. But, then, as rendering continues, our book search query fires, and suspends. What will happen? Will the top-level Suspense boundary continue to show, until everything is ready, or will the lower-down Suspense boundary in books take over?

The answer is the latter. As new Suspense boundaries render lower in the tree, their fallback will replace the fallback of whatever antecedent Suspense fallback was already showing. There's currently an unstable API to override this, but if you're doing a good job of crafting your fallbacks, this is probably the behavior you want. You don't want “Still loading, sorry” to just keep showing. Rather, as soon as the books component is ready, you absolutely want to display that shell with the more targeted waiting message.

Now, what if our books module loads and starts to render while the startTransition spinner is still showing and then suspends? In other words, imagine that our startTransition has a timeout of three seconds, the books component renders, the nested Suspense boundary is in the component tree after one second, and the search query suspends. Will the remaining two seconds elapse before that new nested Suspense boundary renders the fallback, or will the fallback show immediately? The answer, perhaps surprisingly, is that the new Suspense fallback will show immediately by default. That’s because it's best to show a new, valid UI as quickly as possible, so the user can see that things are happening, and progressing. 

How data fits in

Navigation is fine, but how does data loading fit into all of this?

It fits in completely and transparently. Data loading triggers suspensions just like navigation with React.lazy, and it hooks into all the same useTransition and Suspense boundaries. This is what's so amazing about Suspense: all your async dependencies seamlessly work in this same system. Managing these various async requests manually to ensure consistency was a nightmare before Suspense, which is precisely why nobody did it. Web apps were notorious for cascading spinners that stopped at unpredictable times, producing inconsistent UIs that were only partially finished.

OK, but how do we actually tie data loading into this? Data loading in Suspense is paradoxically both more complex, and also simple.

I'll explain.

If you're waiting on data, you'll throw a promise in the component that reads (or attempts to read) the data. The promise should be consistent based on the data request. So, four repeated requests for that same "C++" search query should throw the same, identical promise. This implies some sort of caching layer to manage all this. You'll likely not write this yourself. Instead, you'll just hope, and wait for the data library you use to update itself to support Suspense.

This is already done in my micro-graphql-react library. Instead of using the useQuery hook, you’ll use the useSuspenseQuery hook, which has an identical API, but throws a consistent promise when you're waiting on data.

Wait, what about preloading?!

Has your brain turned to mush reading other things on Suspense that talked about waterfalls, fetch-on-render, preloading, etc? Don't worry about it. Here's what it all means.

Let's say you lazy load the books component, which renders and then requests some data, which causes a new Suspense. The network request for the component and the network request for the data will happen one after the other—in a waterfall fashion.

But here's the key part: the application state that led to whatever initial query that ran when the component loaded was already available when you started loading the component (which, in this case, is the URL). So why not "start" the query as soon as you know you'll need it? As soon as you browse to /books, why not fire off the current search query right then and there, so it's already in flight when the component loads.

The micro-graphql-react module does indeed have a preload method, and I urge you to use it. Preloading data is a nice performance optimization, but it has nothing to do with Suspense. Classic React apps could (and should) preload data as soon as they know they'll need it. Vue apps should preload data as soon as they know they'll need it. Svelte apps should... you get the point.

Preloading data is orthogonal to Suspense, which is something you can do with literally any framework. It’s also something we all should have been doing already, even though nobody else was.

But seriously, how do you preload?

That's up to you. At the very least, the logic to run the current search absolutely needs to be completely separated into its own, standalone module. You should literally make sure this preload function is in a file by itself. Don't rely on webpack to treeshake; you'll likely face abject sadness the next time you audit your bundles.

You have a preload() method in its own bundle, so call it. Call it when you know you're about to navigate to that module. I assume React Router has some sort of API to run code on a navigation change. For the vanilla routing code above, I call the method in that routing switch from before. I had omitted it for brevity, but the books entry actually looks like this:

switch (moduleToLoad.toLowerCase()) {
  case "activate":
    return ActivateComponent;
  case "authenticate":
    return AuthenticateComponent;
  case "books":
    // preload!!!
    booksPreload();
    return BooksComponent;

That's it. Here's a live demo to play around with:

To modify the Suspense timeout value, which defaults to 3000ms, navigate to Settings, and check out the misc tab. Just be sure to refresh the page after modifying it.

Wrapping up

I've seldom been as excited for anything in the web dev ecosystem as I am for Suspense. It's an incredibly ambitious system for managing one of the trickiest problems in web development: asynchrony.

The post React Suspense in Practice appeared first on CSS-Tricks.

The Hooks of React Router

React Router 5 embraces the power of hooks and has introduced four different hooks to help with routing. You will find this article useful if you are looking for a quick primer on the new patterns of React Router. But before we look at hooks, we will start off with a new route rendering pattern.

Before React Router 5

// When you wanted to render the route and get router props for component
<Route path="/" component={Home} />


// Or when you wanted to pass extra props
<Route path="/" render={({ match }) => <Profile match={match} mine={true} />}>

When using the component syntax, route props (match, location and history) are implicitly being passed on to the component. But it has to be changed to render once you have extra props to pass to the component. Note that adding an inline function to the component syntax would lead to the component re-mounting on every render.

After React Router 5

<Route path="/">
  <Home />
</Route>

Note that there is no implicit passing of any props to the Home component. But without changing anything with the Route itself, you can add any extra props to the Home component. You can no longer make the mistake of re-mounting the component on every render and that's the best kind of API.

But now route props are not being passed implicitly, so how do we access match, history or location? Do we have to wrap all components with withRouter? That is where the hooks steps in.

Note that hooks were introduced in 16.8 version of React, so you need to be above that version to use them.

useHistory

  • Provides access to the history prop in React Router
  • Refers to the history package dependency that the router uses
  • A primary use case would be for programmatic routing with functions, like push, replace, etc.
import { useHistory } from 'react-router-dom';

function Home() {
  const history = useHistory();
  return <button onClick={() => history.push('/profile')}>Profile</button>;
}

useLocation

  • Provides access to the location prop in React Router
  • It is similar to window.location in the browser itself, but this is accessible everywhere as it represents the Router state and location.
  • A primary use case for this would be to access the query params or the complete route string.
import { useLocation } from 'react-router-dom';

function Profile() {
  const location = useLocation();
  useEffect(() => {
    const currentPath = location.pathname;
    const searchParams = new URLSearchParams(location.search);
  }, [location]);
  return <p>Profile</p>;
}

Since the location property is immutable, useEffect will call the function every time the route changes, making it perfect to operate on search parameters or current path.

useParams

  • Provides access to search parameters in the URL
  • This was possible earlier only using match.params.
import { useParams, Route } from 'react-router-dom';

function Profile() {
  const { name } = useParams();
  return <p>{name}'s Profile</p>;
}

function Dashboard() {
  return (
    <>
      <nav>
        <Link to={`/profile/ann`}>Ann's Profile</Link>
      </nav>
      <main>
        <Route path="/profile/:name">
          <Profile />
        </Route>
      </main>
    </>
  );
}

useRouteMatch

  • Provides access to the match object
  • If it is provided with no arguments, it returns the closest match in the component or its parents.
  • A primary use case would be to construct nested paths.
import { useRouteMatch, Route } from 'react-router-dom';

function Auth() {
  const match = useRouteMatch();
  return (
    <>
      <Route path={`${match.url}/login`}>
        <Login />
      </Route>
      <Route path={`${match.url}/register`}>
        <Register />
      </Route>
    </>
  );
}

You can also use useRouteMatch to access a match without rendering a Route. This is done by passing it the location argument.

For example, consider that you need your own profile to be rendered at /profile and somebody else's profile if the URL contains the name of the person /profile/dan or /profile/ann. Without using the hook, you would either write a Switch, list both routes and customize them with props. But now, using the hook we can do this:

import {
  Route,
  BrowserRouter as Router,
  Link,
  useRouteMatch,
} from 'react-router-dom';

function Profile() {
  const match = useRouteMatch('/profile/:name');

  return match ? <p>{match.params.name}'s Profile</p> : <p>My own profile</p>;
}

export default function App() {
  return (
    <Router>
      <nav>
        <Link to="/profile">My Profile</Link>
        <br />
        <Link to={`/profile/ann`}>Ann's Profile</Link>
      </nav>
      <Route path="/profile">
        <Profile />
      </Route>
    </Router>
  );
}

You can also use all the props on Route like exact or sensitive as an object with useRouteMatch.

Wrapping up

The hooks and explicit Route comes with a hidden advantage in itself. After teaching these techniques at multiple workshops, I have come to the realization that these help avoid a lot of confusion and intricacies that came with the earlier patterns. There are suddenly far fewer unforced errors. They will surely help make your router code more maintainable and you will find it way easier to upgrade to new React Router versions.

The post The Hooks of React Router appeared first on CSS-Tricks.

Third-Party Components at Their Best

I'm a fan of the componentization of the web. I think it's a very nice way to build a website at just about any scale (except, perhaps, the absolute most basic). There are no shortage of opinions about what makes a good component, but say we scope that to third-party for a moment. That is, components that you just use, rather than components that you build yourself as part of your site's unique setup.

What makes a third-party component good? My favorite attribute of a third-party component is when it takes something hard and makes it easy. Particularly things that recognize and properly handle nuances, or things that you might not even know enough about to get right.

Perhaps you use some component that does pop-up contextual menus for you. It might perform browser edge detection, such as ensuring the menu never appears cut off or off-screen. That's a tricky little bit of programming that you might not get right if you did it yourself — or even forget to do.

I think of the <Link /> component that React Router has or what's used on Gatsby sites. It automatically injects aria-current="page" for you on the links when you're on that page. You can and probably should use that for a styling hook! And you probably would have forgotten to program that if you were handling your own links.

In that same vein, Reach UI Tabs have rigorous accessibility baked into them that you probably wouldn't get right if you hand-rolled them. This React image component does all sorts of stuff that is relatively difficult to pull off with images, like the complex responsive images syntax, lazy loading, placeholders, etc. This is, in a sense, handing you best practices for "free."

Here's a table library that doesn't even touch UI for you, and instead focuses on other needs you're likely to have with tables, which is another fascinating approach.

Anyway! Here's what y'all said when I was asking about this. What makes a third-party component awesome? What do the best of them do? (besides the obvious, like good docs and good accessibility)? Some of these might be at-odds. I'm just listing what people said they like.

  • Plug-and-play. It should "just work" with minimal config.
  • Lots of editable demos
  • Highly configurable
  • "White label" styling. Don't bring too strong of design choices.
  • Styled via regular CSS so you can BYO own styling tools
  • Fast
  • Small
  • Is installable via a package manager
  • Can be manually instantiated
  • Can be given a DOM node where it can go
  • Follows a useful versioning scheme
  • Is manintained, particularly for security
  • Has a public roadmap
  • Is framework-agnostic
  • Doesn't have other dependencies
  • Uses intuitive naming conventions
  • Supports internationalization
  • Has lots of tests

Anything you'd add to that list?

The post Third-Party Components at Their Best appeared first on CSS-Tricks.