Tag: React

Getting Started with React Testing Library

I can guess what you are thinking: another React testing library? So many have already been covered here on CSS-Tricks (heck, I’ve already posted one covering Jest and Enzyme) so aren’t there already enough options to go around?

But react-testing-library is not just another testing library. It’s a testing library, yes, but one that’s built with one fundamental principle that separates it from the rest.

The more your tests resemble the way your software is used, the more confidence they can give you.

It tries to address tests for how a user will use your application. In fact, it’s done in such a way that tests won’t break even when you refactor components. And I know that’s something we’ve all run into at some point in our React journey.

We’re going to spend some time writing tests together using react-testing-library for a light to-do application I built. You can clone the repo locally:

git clone https://github.com/kinsomicrote/todoapp-test.git

And, if you do that, install the required packages next:

## yarn yarn add --dev react-testing-library jest-dom  ## npm npm install --save-dev react-testing-library jest-dom

In case you’re wondering why Jest is in there, we’re using it for assertion. Create a folder called __test__ inside the src directory and create a new file called App.test.js.

Taking snapshots

Snapshot tests keep a record of tests that have been performed on a tested component as a way to visually see what’s changes between changes.

When we first run this test, we take the first snapshot of how the component looks. As such, the first test is bound to pass because, well, there’s no other snapshot to compare it to that would indicate something failed. It only fails when we make a new change to the component by adding a new element, class, component, or text. Adding something that was not there when the snapshot was either created or last updated.

The snapshot test will be the first test we will be writing here. Let’s open the App.test.js file and make it look like this:

import React from 'react'; import { render, cleanup } from "react-testing-library"; import "jest-dom/extend-expect"; import App from './App';  afterEach(cleanup);  it("matches snapshot", () => {   const { asFragment } = render(<App />);   expect(asFragment()).toMatchSnapshot(); });

This imports the necessary packages we are using to write and run the tests. render is used to display the component we want to test. We make use of cleanup to clear things out after each test runs — as you can see with the afterEach(cleanup) line.

Using asFragment, we get a DocumentFragment of the rendered component. Then we expect it to match the snapshot that had been created.

Let’s run the test to see what happens:

## yarn yarn test  ## npm npm test

As we now know, a snapshot of the component gets created in a new folder called __snapshots__ inside the __tests__ directory if this is our first test. We actually get a file called App.test.js.snap in there that will look like this:

// Jest Snapshot v1, https://goo.gl/fbAQLP  exports[`matches snapshot 1`] = ` <DocumentFragment>   <div     class="container"   >     <div       class="row"     >       <div         class="col-md-6"       >         <h2>           Add Todo         </h2>       </div>     </div>     <form>       <div         class="row"       >         <div           class="col-md-6"         >           <input             class="form-control"             data-testid="todo-input"             placeholder="Enter a task"             type="text"             value=""           />         </div>       </div>       <div         class="row"       >         <div           class="col-md-6"         >           <button             class="btn btn-primary"             data-testid="add-task"             type="submit"           >             Add Task           </button>         </div>       </div>     </form>     <div       class="row todo-list"     >       <div         class="col-md-6"       >         <h3>           Lists         </h3>         <ul           data-testid="todos-ul"         >           <li>             <div>               Buy Milk               <button                 class="btn btn-danger"               >                 X               </button>             </div>           </li>           <li>             <div>               Write tutorial               <button                 class="btn btn-danger"               >                 X               </button>             </div>           </li>         </ul>       </div>     </div>   </div> </DocumentFragment> `;

Now, let’s Test DOM elements and events

Our app includes two to-do items that display by default the first time the app runs. We want to make sure that they do, in fact, show up by default on the first app run so, to test this, we have to target the unordered list (<ul>) and check the length. We expect the length to be equal to two — the number of items.

it('it displays default todo items', () => {   const { getByTestId } = render(<App />);   const todoList = getByTestId('todos-ul');   expect(todoList.children.length).toBe(2);   });

We’re making use of getByTestId in that snippet to extract the test IDs from the App component. We then set todoList to target the todos-ul element. That’s what should return as two.

Using what we’ve learned so far, see if you can write a test to assert that a user can enter values in the input field. Here are the things you’ll want to do:

  • Get the input field
  • Set a value for the input field
  • Trigger a change event
  • Assert that the input field has its value as the one you set for it in Step 2

Don’t peek at my answer below! Take as much time as you need.

Still going? Great! I’ll go grab some coffee and be right back.

Mmm, coffee. ☕️

Oh, you’re done! You rock. Let’s compare answers. Mine looks like this:

it('allows input', () => {   const {getByTestId } = render(<App />)   let item = 'Learn React'   const todoInputElement = getByTestId('todo-input');   todoInputElement.value = item;   fireEvent.change(todoInputElement);   expect(todoInputElement.value).toBe('Learn React') });

Using getByTestId, I am able to extract the test IDs in the application. Then I create a variable which is set to the string Learn React, and make it the value of the input field. Next, I obtain the input field using its test ID and fire the change event after setting the value of the input field. With that done, I assert that the value of the input field is indeed Learn React.

Does that check out with your answer? Leave a comment if you have another way of going about it!

Next, let’s test that we can add a new to-do item. We’ll need to get the input field, the button for adding new items and the unordered list because those are all of the elements needed to create an new item.

We set a value for the input field and then trigger a button click to add the task. We’re able to do this by obtaining the button using getByText — by triggering a click event on the DOM element with the text Add Task, we should be able to add a new to-do item.

Let’s assert that the number of children (list items) in unordered list element is equal to three. This assumes that the default tasks are still in tact.

it('adds a new todo item', () => {   const { getByText, getByTestId } = render(<App />);   const todoInputElement = getByTestId('todo-input');   const todoList = getByTestId('todos-ul');   todoInputElement.value = 'Learn React';   fireEvent.change(todoInputElement);   fireEvent.click(getByText('Add Task'))   expect(todoList.children.length).toBe(3);  });

Pretty nice, right?

This is just one way to test in React

You can try react-testing-library in your next React application. The documentation in the repo is super thorough and — like most tools — the best place to start. Kent C. Dodds built it and has a full course on testing over at Frontend Masters (subscription required) that also covers the ins and outs of react-testing-library.

That said, this is just one testing resource for React. There are others, of course, but hopefully this is one you’re interested in trying out now that you’ve seen a bit of it but use what’s best for your project, of course.

The post Getting Started with React Testing Library appeared first on CSS-Tricks.

CSS-Tricks

, , , ,

Iterating a React Design with Styled Components

In a perfect world, our projects would have unlimited resources and time. Our teams would begin coding with well thought out and highly refined UX designs. There would be consensus among developers about the best way to approach styling. There’d be one or more CSS gurus on the team who could ensure that functionality and style could roll out simultaneously without it turning into a train-wreck.

I’ve actually seen this happen in large enterprise environments. It’s a beautiful thing. This article is not for those people.

On the flip side of the coin is the tiny startup that has zero funding, one or two front-end developers, and a very short timeline to demonstrate some functionality. It doesn’t have to look perfect, but it should at least render reasonably well on desktop, tablet, and mobile. This gets them to a point where it can be shown to advisors and early users; maybe even potential investors who’ve expressed an interest in the concept. Once they get some cashflow from sales and/or investment, they can get a dedicated UX designer and polish the interface.

What follows is for this latter group.

Project Kickoff Meeting

Let’s invent a company to get the ball rolling.

Solar Excursions is a small travel agency aiming to serve the near-future’s burgeoning space tourism industry.

Our tiny development team has agreed that React will be used for the UI. One of our front-end developers is big on Sass, and the other is enamored with CSS in JavaScript. But they’ll be hard pressed to knock out their initial sprint goals; there’s certainly no time for arguing about the best possible styling approach. Both coders agree the choice doesn’t matter much in the long run, as long as its consistently executed. They’re certain that implementing the styling from scratch under the gun now will incur technical debt that will have to be cleaned up later.

After some discussion, the team opts to plan for one or more “styling refactor” sprints. For now, we’ll just focus on getting something up on the screen using React-Bootstrap. That way we’ll be able to quickly build working desktop and mobile layouts without much fuss.

The less time spent on front-end styling the better, because we’ll also need the UI to hook up to the services our backend developer will be cranking out. And, as our application architecture begins to take shape, both front-enders agree it’s important that it be unit tested. They have a lot on their plate.

Based on my discussions with the Powers That Be, as a dedicated project manager, I slaved over Balsamiq for at least ten minutes to provide the team with mockups for the booking page on desktop and mobile. I assume they’ll make tablet meet in the middle and look reasonable.

A desktop and mobile mockup of the proposed layout for the page, side by side with the desktop mockup on the left. Both mockups use rough black and white layouts of the various components for the screen.
Initial mockups for the Solar Excursions Trip Booking Page on desktop (left) and mobile (right).

Sprint Zero: Review Meeting

Pizza all around! The team worked really hard to hit its goals, and we now have a booking page with a layout that approximates the mockups. The infrastructure for services is coming together, but there’s quite a way to go before we can connect the UI to it. In the interim, the front-enders are using a hardcoded mock data structure.

Screenshots of the page after the first round of development on desktop (left) and mobile (right). Both are approximately the same as the earlier mockups with most components looking exactly like the default user interface provided by the Bootstrap framework.
The first iteration of the page in code using react-bootstrap.

Here’s a look at our UI code so far:

This is all straightforward React. We’re using some of that Hooks hotness, but it’s probably passé to most of you by now.

The key takeaway to notice here is how four of our five application components import and use components from react-bootstrap. Only the main App component is unaffected. That’s because it just composes the top level view with our custom components.

// App.js imports import React, { useState } from "react"; import Navigation from "./Navigation"; import Page from "./Page";  // Navigation.js imports import React from "react"; import { Navbar, Dropdown, Nav } from "react-bootstrap";  // Page.js imports import React from "react"; import PosterCarousel from "./PosterCarousel"; import DestinationLayout from "./DestinationLayout"; import { Container, Row, Col } from "react-bootstrap";  // PosterCarousel.js imports import React from "react"; import { Alert, Carousel, Image } from "react-bootstrap";  // DestinationLayout.js imports import React, { useState, useEffect } from "react"; import {   Button,   Card,   Col,   Container,   Dropdown,   Jumbotron,   ListGroup,   Row,   ToggleButtonGroup,   ToggleButton } from "react-bootstrap";

The decision to move fast with Bootstrap has allowed us to hit our sprint goals, but we’re already accumulating technical debt. This is just four affected components, but as the application grows, it’s clear the “styling refactor” sprints that we planned for are going to become exponentially harder. And we haven’t even customized these components much. Once we have tens of components, all using Bootstrap with lots of inline styling to pretty them up, refactoring them to remove react-bootstrap dependencies will be a scary proposition indeed.

Rather than building more of the booking pipeline pages, the team decides that we’ll spend the next sprint working to isolate the react-bootstrap usage in a custom component kit since our services are still under construction. Application components will only use components from this kit. That way, when it comes time to ween ourselves from react-bootstrap, the process will be much easier. We won’t have to refactor thirty usages of the react-bootstrap Button throughout the app, we’ll just rewrite the internals of our KitButton component.

Sprint One: Review Meeting

Well, that was easy. High-fives. No change to the visual appearance of the UI, but we now have a “kit” folder that’s sibling to “components” in our React source. It has a bunch of files like KitButton.js, which basically export renamed react-bootstrap components.

An example component from our kit looks like this:

// KitButton.js import { Button, ToggleButton, ToggleButtonGroup } from "react-bootstrap"; export const KitButton = Button; export const KitToggleButton = ToggleButton; export const KitToggleButtonGroup = ToggleButtonGroup;

We wrap those all kit components up into a module like this:

// kit/index.js import { KitCard } from "./KitCard"; import { KitHero } from "./KitHero"; import { KitList } from "./KitList"; import { KitImage } from "./KitImage"; import { KitCarousel } from "./KitCarousel"; import { KitDropdown } from "./KitDropdown"; import { KitAttribution } from "./KitAttribution"; import { KitNavbar, KitNav } from "./KitNavbar"; import { KitContainer, KitRow, KitCol } from "./KitContainer"; import { KitButton, KitToggleButton, KitToggleButtonGroup } from "./KitButton"; export {   KitCard,   KitHero,   KitList,   KitImage,   KitCarousel,   KitDropdown,   KitAttribution,   KitButton,   KitToggleButton,   KitToggleButtonGroup,   KitContainer,   KitRow,   KitCol,   KitNavbar,   KitNav };

And now our application components are completely free of react-bootstrap. Here are the imports for the affected components:

// Navigation.js imports import React from "react"; import { KitNavbar, KitNav, KitDropdown } from "../kit";   // Page.js imports  import React from "react"; import PosterCarousel from "./PosterCarousel"; import DestinationLayout from "./DestinationLayout"; import { KitContainer, KitRow, KitCol } from "../kit";   // PosterCarousel.js imports import React from "react"; import { KitAttribution, KitImage, KitCarousel } from "../kit";   // DestinationLayout.js imports import React, { useState, useEffect } from "react"; import {   KitCard,   KitHero,   KitList,   KitButton,   KitToggleButton,   KitToggleButtonGroup,   KitDropdown,   KitContainer,   KitRow,   KitCol } from "../kit";

Here’s the front-end codebase now:

Although we’ve corralled all of the react imports into our kit components, our application components still rely a bit on the react-bootstrap implementation because the attributes we place on our kit component instances are the same as those of react-bootstrap. That constrains us when it comes to re-implementing the kit components, because we need to adhere to the same API. For instance:

// From Navigation.js <KitNavbar bg="dark" variant="dark" fixed="top">

Ideally, we wouldn’t have to add those react-bootstrap specific attributes when we instantiate our KitNavbar.

The front-enders promise to refactor those out as we go, now that we’ve identified them as problematic. And any new references to react-bootstrap components will go into our kit instead of directly into the application components.

Meanwhile, we’ve shared our mock data with the server engineer, who is working hard to build separate server environments, implement the database schema, and expose some services to us.

That gives us time to add some gloss to our UI in the next sprint — which is good because the Powers That Be would like to see separate themes for each destination. As the user browses destinations, we need to have the UI color scheme change to match the displayed travel poster. Also, we want to try and spiff up those components a bit to begin evolving our own look and feel. Once we have some money coming in, we’ll get a designer to do a complete overhaul, but hopefully we can reach a happy medium for our early users.

Sprint Two: Review Meeting

Wow! The team really pulled out all the stops this sprint. We got per-destination themes, customized components, and a lot of the lingering react-bootstrap API implementations removed from the application components.

Here’s what the desktop looks like now:

A more complete rendering of the landing page, this time using Mars as an example to show off a bright orange and red color theme. The interface components no longer look like they came directly from Bootstrap, but are still minimal in style, like dropdowns, buttons and text.
Check out the solarized theme for the red planet!

In order to pull this off, the front-enders brought in the Styled Components library. It made styling the individual kit components a breeze, as well as adding support for multiple themes.

Let’s look at a few highlights of their changes for this sprint.

First, for global things like pulling in fonts and setting the page body styles, we have a new kit component called KitGlobal.

// KitGlobal.js import { createGlobalStyle } from "styled-components"; export const KitGlobal = createGlobalStyle`   body {     @import url('https://fonts.googleapis.com/css?family=Orbitron:500|Nunito:600|Alegreya+Sans+SC:700');     background-color: $ {props => props.theme.foreground};     overflow-x: hidden;   } `;

It uses the createGlobalStyle helper to define the CSS for the body element. That imports our desired web fonts from Google, sets the background color to whatever the current theme’s “foreground” value is, and turns off overflow in the x-direction to eliminate a pesky horizontal scrollbar. We use that KitGlobal component in the render method of our App component.

Also in the App component, we import ThemeProvider from styled-components, and something called “themes” from ../theme. We use React’s useState to set the initial theme to themes.luna and React’s useEffect to call setTheme whenever the “destination” changes. The returned component is now wrapped in ThemeProvider, which is passed “theme” as a prop. Here’s the App component in its entirety.

// App.js import React, { useState, useEffect } from "react"; import { ThemeProvider } from "styled-components"; import themes from "../theme/"; import { KitGlobal } from "../kit"; import Navigation from "./Navigation"; import Page from "./Page"; export default function App(props) {   const [destinationIndex, setDestinationIndex] = useState(0);   const [theme, setTheme] = useState(themes.luna);   const destination = props.destinations[destinationIndex];   useEffect(() => {     setTheme(themes[destination.theme]);   }, [destination]);    return (     <ThemeProvider theme={theme}>       <React.Fragment>         <KitGlobal />         <Navigation           {...props}           destinationIndex={destinationIndex}           setDestinationIndex={setDestinationIndex}         />         <Page           {...props}           destinationIndex={destinationIndex}           setDestinationIndex={setDestinationIndex}         />       </React.Fragment>     </ThemeProvider>   ); }

KitGlobal is rendering like any other component. Nothing special there, only that the body tag is affected. ThemeProvider is using the React Context API to pass theme down to whatever components need it (which is all of them). In order to fully understand that, we also need to take a look at what a theme actually is.

To create a theme, one of our front-enders took all the travel posters and created palettes for each by extracting the prominent colors. That was fairly simple.

A screenshot of the Tiny Eye website showing the red color palette used for the Mars page theme. There are two images on the left from the page that Tiny Eye used to create a color palette in the center containing various shades of red and the hex values for them on the right.
We used TinyEye for this.

Obviously, we weren’t going to use all the colors. The approach was mainly to dub the most used two colors foreground and background. Then we took three more colors, generally ordered from lightest to darkest as accent1, accent2, and accent3. Finally, we picked two contrasting colors to call text1 and text2. For the above destination, that looked like:

// theme/index.js (partial list) const themes = {   ...   mars: {     background: "#a53237",     foreground: "#f66f40",     accent1: "#f8986d",     accent2: "#9c4952",     accent3: "#f66f40",     text1: "#f5e5e1",     text2: "#354f55"   },   ... }; export default themes;

Once we have a theme for each destination, and it is being passed into all the components (including the kit components that our application components are now built from), we need to use styled-components to apply those theme colors as well as our custom visual styling, like the panel corners and “border glow.”

This is a simple example where we made our KitHero component apply the theme and custom styles to the Bootstrap Jumbotron:

// KitHero.js import styled from "styled-components"; import { Jumbotron } from "react-bootstrap";  export const KitHero = styled(Jumbotron)`   background-color: $ {props => props.theme.accent1};   color: $ {props => props.theme.text2};   border-radius: 7px 25px;   border-color: $ {props => props.theme.accent3};   border-style: solid;   border-width: 1px;   box-shadow: 0 0 1px 2px #fdb813, 0 0 3px 4px #f8986d;   font-family: "Nunito", sans-serif;   margin-bottom: 20px; `;

In this case, we’re good to go with what gets returned from styled-components, so we just name it KitHero and export it.

When we use it in the application, it looks like this:

// DestinationLayout.js (partial code) const renderHero = () => {   return (     <KitHero>       <h2>{destination.header}</h2>       <p>{destination.blurb}</p>       <KitButton>Book Your Trip Now!</KitButton>     </KitHero>   ); };

Then there are more complex cases where we want to preset some attributes on the react-bootstrap component. For instance, the KitNavbar component which we identified earlier as having a bunch of react-bootstrap attributes that we’d rather not pass from the application’s declaration of the component.

Now for a look at how that was handled:

// KitNavbar.js (partial code) import React, { Component } from "react"; import styled from "styled-components"; import { Navbar } from "react-bootstrap";  const StyledBootstrapNavbar = styled(Navbar)`   background-color: $ {props => props.theme.background};   box-shadow: 0 0 1px 2px #fdb813, 0 0 3px 4px #f8986d;   display: flex;   flex-direction: horizontal;   justify-content: space-between;   font-family: "Nunito", sans-serif; `;  export class KitNavbar extends Component {   render() {     const { ...props } = this.props;     return <StyledBootstrapNavbar fixed="top" {...props} />;   } }

First, we create a component called StyledBootstrapNavbar using styled-components. We were able to handle some of the attributes with the CSS we passed to styled-components. But in order to continue leveraging (for now) the reliable stickiness of the component to the top of the screen while everything else is scrolled, our front-enders elected to continue using react-bootstrap’s fixed attribute. In order to do that, we had to create a KitNavbar component that rendered an instance of StyledBootstrapNavbar with the fixed=top attribute. We also passed through all the props, which includes its children.

We only have to create a separate class that renders styled-component’s work and passes props through to it if we want to explicitly set some attributes in our kit component by default. In most cases, we can just name and return styled-component’s output and use it as we did with KitHero above.

Now, when we render the KitNavbar in our application’s Navigation component, it looks like this:

// Navigation.js (partial code) return (   <KitNavbar>     <KitNavbarBrand>       <KitLogo />       Solar Excursions     </KitNavbarBrand>     {renderDestinationMenu()}   </KitNavbar> );

Finally, we took our first stabs at refactoring our kit components away from react-bootstrap. The KitAttribution component is a Bootstrap Alert which, for our purposes, is little more than an ordinary div. We were able to easily refactor to remove its dependency on react-bootstrap.

This is the component as it emerged from the previous sprint:

// KitAttribution.js (using react-bootstrap) import { Alert } from "react-bootstrap"; export const KitAttribution = Alert;

This is what it looks like now:

// KitAttribution.js import styled from "styled-components"; export const KitAttribution = styled.div`   text-align: center;   background-color: $ {props => props.theme.accent1};   color: $ {props => props.theme.text2};   border-radius: 7px 25px;   border-color: $ {props => props.theme.accent3};   border-style: solid;   border-width: 1px;   box-shadow: 0 0 1px 2px #fdb813, 0 0 3px 4px #f8986d;   font-family: "Alegreya Sans SC", sans-serif;   > a {     color: $ {props => props.theme.text2};     font-family: "Nunito", sans-serif;   }   > a:hover {     color: $ {props => props.theme.background};     text-decoration-color: $ {props => props.theme.accent3};   } `;

Notice how we no longer import react-bootstrap and we use styled.div as the component base. They won’t all be so easy, but it’s a process.

Here are the results of our team’s styling and theming efforts in sprint 3:

Conclusion

After three sprints, our team is well on its way to having a scalable component architecture in place for the UI.

  • We are moving quickly thanks to react-bootstrap, but are no longer piling up loads of technical debt as a result of it.
  • Thanks to styled-components, we were able to implement multiple themes (like how almost every app on the Internet these days sports dark and light modes). We also don’t look like an out-of-the-box Bootstrap app anymore.
  • By implementing a custom component kit that contains all references to react-bootstrap, we can refactor away from it as time permits.

The post Iterating a React Design with Styled Components appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Using Parcel as a Bundler for React Applications

You may already be familiar with webpack for asset management on projects. However, there’s another cool tool out there called Parcel, which is comparable to webpack in that it helps with hassle-free asset bundling. Where Parcel really shines is that it requires zero configuration to get up and running, where other bundlers often require writing a ton code just to get started. Plus, Parcel is super fast when it runs because it utilizes multicore processing where others work off of complex and heavy transforms.

So, in a nutshell, we’re looking at a number of features and benefits:

  • Code splitting using dynamic imports
  • Assets handling for any type of file, but of course for HTML, CSS and JavaScript
  • Hot Module Replacement to update elements without a page refresh during development
  • Mistakes in the code are highlighted when they are logged, making them easy to locate and correct
  • Environment variables to easily distinguish between local and production development
  • A “Production Mode” that speeds up the build by preventing unnecessary build steps

Hopefully, you’re starting to see good reasons for using Parcel. That’s not to say it should be used 100% or all the time but rather that there are good cases where it makes a lot of sense.

In this article, we’re going to see how to set up a React project using Parcel. While we’re at it, we’ll also check out an alternative for Create React App that we can use with Parcel to develop React applications. The goal here is see that there are other ways out there to work in React, using Parcel as an example.

Setting up a new project

OK, the first thing we need is a project folder to work locally. We can make a new folder and navigate to it directly from the command line:

mkdir csstricks-react-parcel && $  _

Next, let’s get our obligatory package.json file in there. We can either make use of npm or Yarn by running one of the following:

## Using npm npm init -y  ## Using Yarn, which we'll continue with throughout the article yarn init -y

This gives us a package.json file in our project folder containing the default configurations we need to work locally. Speaking of which, the parcel package can be installed globally, but for this tutorial, we’ll install it locally as a dev dependency.

We need Babel when working in React, so let’s get that going:

yarn add parcel-bundler babel-preset-env babel-preset-react --dev

Next, we install React and ReactDOM…

yarn add react react-dom

…then create a babel.rc file and add this to it:

{   "presets": ["env", "react"] }

Next, we create our base App component in a new index.js file. Here’s a quick one that simply returns a “Hello” heading:

import React from 'react' import ReactDOM from 'react-dom' class App extends React.Component {   render() {     return (       <React.Fragment>         <h2>Hello!</h2>       </React.Fragment>     )   } }  const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

We’ll need an HTML file where the App component will be mounted, so let’s create an index.html file inside the src directory. Again, here’s a pretty simple shell to work off of:

<html lang="en">   <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1.0">     <meta http-equiv="X-UA-Compatible" content="ie=edge">     <title>Parcel React Example</title>   </head>   <body>     <div id="root"></div>     <script src="./index.js"></script>   </body> </html>

We will make use of the Parcel package we installed earlier. For that to work, we need to edit the start script in package.json file so it looks like this:

"scripts": {   "start": "NODE_ENV=development parcel src/index.html --open" }

Finally, let’s go back to the command line and run yarn start. The application should start and open up a fresh browser window pointing at http://localhost:1234/.

Working with styles

Parcel ships with PostCSS out of the box but, if we wanted to work with something else, we can totally do that. For example, we can install node-sass to use Sass on the project:

yarn add --dev node-sass autoprefixer

We already have autoprefixer since it’s a PostCSS plugin, so we can configure that in the postcss block of package.json:

// ... "postcss": {   "modules": false,   "plugins": {     "autoprefixer": {       "browsers": [">1%", "last 4 versions", "Firefox ESR", "not ie < 9"],       "flexbox": "no-2009"     }   } }

Setting up a production environment

We’re going to want to make sure our code and assets are compiled for production use, so let’s make sure we tell our build process where those will go. Again, in package-json:

"scripts": {   "start": "NODE_ENV=development parcel src/index.html --open",   "build": "NODE_ENV=production parcel build dist/index.html --public-url ./" }

Running the yarn run build will now build the application for production and output it in the dist folder. There are some additional options we can add to refine things a little further if we’d like:

  • --out-dir directory-name: This is for using another directory for the production files instead of the default dist directory.
  • --no-minify: Minification is enabled by default, but we can disable with this command.
  • --no-cache: This allows us to disable filesystem cache.

💩 CRAP (Create React App Parcel)

Create React App Parcel (CRAP) is a package built by Shawn Swyz Wang to help quickly set up React applications for Parcel. According to the documentation, we can bootstrap any application by running this:

npx create-react-app-parcel my-app

That will create the files and directories we need to start working. Then, we can migrate over to the application folder and start the server.

cd my-app yarn start

Parcel is all set up!

Parcel is worth exploring in your next React application. The fact that there’s no required configuration and that the bundle time is super optimized makes Parcel worth considering on a future project. And, with more than 30,000 stars on GitHub, it looks like something that’s getting some traction in the community.

Additional resources

  • Parcel Examples: Parcel examples using various tools and frameworks.
  • Awesome Parcel: A curated list of awesome Parcel resources, libraries, tools and boilerplates.

The source code for this tutorial is available on GitHub

The post Using Parcel as a Bundler for React Applications appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

The Circle of a React Lifecycle

A React component goes through different phases as it lives in an application, though it might not be evident that anything is happening behind the scenes.

Those phases are:

  • mounting
  • updating
  • unmounting
  • error handling

There are methods in each of these phases that make it possible to perform specific actions on the component during that phase. For example, when fetching data from a network, you’d want to call the function that handles the API call in the componentDidMount() method, which is available during the mounting phase.

Knowing the different lifecycle methods is important in the development of React applications, because it allows us to trigger actions exactly when they’re needed without getting tangled up with others. We’re going to look at each lifecycle in this post, including the methods that are available to them and the types of scenarios we’d use them.

The Mounting Phase

Think of mounting as the initial phase of a component’s lifecycle. Before mounting occurs, a component has yet to exist — it’s merely a twinkle in the eyes of the DOM until mounting takes place and hooks the component up as part of the document.

There are plenty of methods we can leverage once a component is mounted: constructor() , render(), componentDidMount() and static getDerivedStateFromProps(). Each one is handy in it’s own right, so let’s look at them in that order.

constructor()

The constructor() method is expected when state is set directly on a component in order to bind methods together. Here is how it looks:

// Once the input component is mounting... constructor(props) {   // ...set some props on it...   super(props);   // ...which, in this case is a blank username...   this.state = {     username: ''   };   // ...and then bind with a method that handles a change to the input   this.handleInputChange = this.handleInputChange.bind(this); }

It is important to know that the constructor is the first method that gets called as the component is created. The component hasn’t rendered yet (that’s coming) but the DOM is aware of it and we can hook into it before it renders. As a result, this isn’t the place where we’d call setState() or introduce any side effects because, well, the component is still in the phase of being constructed!

I wrote up a tutorial on refs a little while back, and once thing I noted is that it’s possible to set up ref in the constructor when making use of React.createRef(). That’s legit because refs is used to change values without props or having to re-render the component with updates values:

constructor(props) {   super(props);   this.state = {     username: ''   };   this.inputText = React.createRef(); }

render()

The render() method is where the markup for the component comes into view on the front end. Users can see it and access it at this point. If you’ve ever created a React component, then you’re already familiar with it — even if you didn’t realize it — because it’s required to spit out the markup.

class App extends React.Component {   // When mounting is in progress, please render the following!   render() {     return (       <div>         <p>Hello World!</p>       </div>     )   } }

But that’s not all that render() is good for! It can also be used to render an array of components:

class App extends React.Component {   render () {     return []       <h2>JavaScript Tools</h2>,       <Frontend />,       <Backend />     ]   } }

…and even fragments of a component:

class App extends React.Component {   render() {     return (       <React.Fragment>         <p>Hello World!</p>       </React.Fragment>     )   } }

We can also use it to render components outside of the DOM hierarchy (a la React Portal):

// We're creating a portal that allows the component to travel around the DOM class Portal extends React.Component {   // First, we're creating a div element   constructor() {     super();     this.el = document.createElement("div");   }      // Once it mounts, let's append the component's children   componentDidMount = () => {     portalRoot.appendChild(this.el);   };      // If the component is removed from the DOM, then we'll remove the children, too   componentWillUnmount = () => {     portalRoot.removeChild(this.el);   };      // Ah, now we can render the component and its children where we want   render() {     const { children } = this.props;     return ReactDOM.createPortal(children, this.el);   } }

And, of course, render() can — ahem — render numbers and strings…

class App extends React.Component {   render () {     return "Hello World!"   } }

…as well as null or Boolean values:

class App extends React.Component {   render () {     return null   } }

componentDidMount()

Does the componentDidMount() name give away what it means? This method gets called after the component is mounted (i.e. hooked to the DOM). In another tutorial I wrote up on fetching data in React, this is where you want to make a request to obtain data from an API.

We can have your fetch method:

fetchUsers() {   fetch(`https://jsonplaceholder.typicode.com/users`)     .then(response => response.json())     .then(data =>       this.setState({         users: data,         isLoading: false,       })     )   .catch(error => this.setState({ error, isLoading: false })); }

Then call the method in componentDidMount() hook:

componentDidMount() {   this.fetchUsers(); }

We can also add event listeners:

componentDidMount() {   el.addEventListener() }

Neat, right?

static getDerivedStateFromProps()

It’s kind of a long-winded name, but static getDerivedStateFromProps() isn’t as complicated as it sounds. It’s called before the render() method during the mounting phase, and before the update phase. It returns either an object to update the state of a component, or null when there’s nothing to update.

To understand how it works, let’s implement a counter component which will have a certain value for its counter state. This state will only update when the value of maxCount is higher. maxCount will be passed from the parent component.

Here’s the parent component:

class App extends React.Component {   constructor(props) {     super(props)          this.textInput = React.createRef();     this.state = {       value: 0     }   }      handleIncrement = e => {     e.preventDefault();     this.setState({ value: this.state.value + 1 })   };    handleDecrement = e => {     e.preventDefault();     this.setState({ value: this.state.value - 1 })   };    render() {     return (       <React.Fragment>         <section className="section">           <p>Max count: { this.state.value }</p>           <button             onClick={this.handleIncrement}             class="button is-grey">+</button>           <button             onClick={this.handleDecrement}             class="button is-dark">-</button>                   </section>         <section className="section">           <Counter maxCount={this.state.value} />         </section>       </React.Fragment>     )   } }

We have a button used to increase the value of maxCount, which we pass to the Counter component.

class Counter extends React.Component {   state={     counter: 5   }    static getDerivedStateFromProps(nextProps, prevState) {     if (prevState.counter < nextProps.maxCount) {       return {         counter: nextProps.maxCount       };     }      return null;   }    render() {     return (       <div className="box">         <p>Count: {this.state.counter}</p>       </div>     )   } }

In the Counter component, we check to see if counter is less than maxCount. If it is, we set counter to the value of maxCount. Otherwise, we do nothing.

You can play around with the following Pen below to see how that works on the front end:

See the Pen
getDerivedStateFromProps
by Kingsley Silas Chijioke (@kinsomicrote)
on CodePen.


The Updating Phase

The updating phase occurs when a component when a component’s props or state changes. Like mounting, updating has its own set of available methods, which we’ll look at next. That said, it’s worth noting that both render() and getDerivedStateFromProps() also get triggered in this phase.

shouldComponentUpdate()

When the state or props of a component changes, we can make use of the shouldComponentUpdate() method to control whether the component should update or not. This method is called before rendering occurs and when state and props are being received. The default behavior is true. To re-render every time the state or props change, we’d do something like this:

shouldComponentUpdate(nextProps, nextState) {   return this.state.value !== nextState.value; }

When false is returned, the component does not update and, instead, the render() method is called to display the component.

getSnapshotBeforeUpdate()

One thing we can do is capture the state of a component at a moment in time, and that’s what getSnapshotBeforeUpdate() is designed to do. It’s called after render() but before any new changes are committed to the DOM. The returned value gets passed as a third parameter to componentDidUpdate().

It takes the previous state and props as parameters:

getSnapshotBeforeUpdate(prevProps, prevState) {   // ... }

Use cases for this method are kinda few and far between, at least in my experience. It is one of those lifecycle methods you may not find yourself reaching for very often.

componentDidUpdate()

Add componentDidUpdate() to the list of methods where the name sort of says it all. If the component updates, then we can hook into it at that time using this method and pass it previous props and state of the component.

componentDidUpdate(prevProps, prevState) {   if (prevState.counter !== this.state.counter) {     // ...   } }

If you ever make use of getSnapshotBeforeUpdate(), you can also pass the returned value as a parameter to componentDidUpdate():

componentDidUpdate(prevProps, prevState, snapshot) {   if (prevState.counter !== this.state.counter) {     // ....   } }

The Unmounting Phase

We’re pretty much looking at the inverse of the mounting phase here. As you might expect, unmounting occurs when a component is wiped out of the DOM and no longer available.

We only have one method in here: componentWillUnmount()

This gets called before a component is unmounted and destroyed. This is where we would want to carry out any necessary clean up after the component takes a hike, like removing event listeners that may have been added in componentDidMount(), or clearing subscriptions.

// Remove event listener  componentWillUnmount() {   el.removeEventListener() }

The Error Handling Phase

Things can go wrong in a component and that can leave us with errors. We’ve had error boundary around for a while to help with this. This error boundary component makes use of some methods to help us handle the errors we could encounter.

getDerivedStateFromError()

We use getDerivedStateFromError() to catch any errors thrown from a descendant component, which we then use to update the state of the component.

class ErrorBoundary extends React.Component {    constructor(props) {     super(props);     this.state = {       hasError: false     };   }    static getDerivedStateFromError(error) {     return { hasError: true };   }      render() {     if (this.state.hasError) {       return (         <h1>Oops, something went wrong :(</h1>       );     }      return this.props.children;   } }

In this example, the ErrorBoundary component will display “Oops, something went wrong” when an error is thrown from a child component. We have a lot more info on this method in a wrap up on goodies that were released in React 16.6.0.

componentDidCatch()

While getDerivedStateFromError() is suited for updating the state of the component in cases where where side effects, like error logging, take place, we ought to make use of componentDidCatch() because it is called during the commit phase, when the DOM has been updated.

componentDidCatch(error, info) {   // Log error to service }

Both getDerivedStateFromError() and componentDidCatch() can be used in the ErrorBoundary component:

class ErrorBoundary extends React.Component {    constructor(props) {   super(props);   this.state = {     hasError: false   }; }    static getDerivedStateFromError(error) {     return { hasError: true };   }    componentDidCatch(error, info) {     // Log error to service   }      render() {     if (this.state.hasError) {       return (         <h1>Oops, something went wrong :(</h1>       );     }      return this.props.children;   } }

And that’s the lifecycle of a React component!

There’s something neat about knowing how a React component interacts with the DOM. It’s easy to think some “magic” happens and then something appears on a page. But the lifecycle of a React component shows that there’s order to the madness and it’s designed to give us a great deal of control to make things happen from the time the component hits the DOM to the time it goes away.

We covered a lot of ground in a relatively short amount of space, but hopefully this gives you a good idea of not only how React handles components, but what sort of capabilities we have at various stages of that handling. Feel free to leave any questions at all if anything we covered here is unclear and I’d be happy to help as best I can!

The post The Circle of a React Lifecycle appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Accessibility is not a “React Problem”

Leslie Cohn-Wein’s main point:

While [lots of divs, inline styles, focus management problems] are valid concerns, it should be noted that nothing in React prevents us from building accessible web apps.

True. I’m quite capable (and sadly, guilty) of building inaccessible interfaces with React or without.

I’ve long told people that one way to level up your front-end design and development skills, especially in your early days, is to understand how to change classes. I can write a few lines of JavaScript to add/remove an active class and build a tabbed interface quite quickly. But did I build the HTML in such a way that it’s accessible by default? Did I deal with keyboard events? Did I deal with all the relevant aria-* attributes? I’ll answer for myself here: no. I’ve gotten better about it over time, but sadly my muscle memory for the correct pattern isn’t always there.

I also tend to listen when folks I trust who specialize in accessibility say that the proliferation of SPAs, of which React is a major player, conspicuously coincides with a proliferation of accessibility issues.

I’m optimistic though. For example, React has a blessed tabs solution that is accessible out of the box. I reach for those, and thus my muscle memory for building tabs now results in a more accessible product. And when I to routing/linking with Reach Router, I get accessibility “baked in,” as they say. That’s a powerful thing to get “for free,” again, as they say.

Direct Link to ArticlePermalink

The post Accessibility is not a “React Problem” appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Using React Loadable for Code Splitting by Components and Routes

In a bid to have web applications serve needs for different types of users, it’s likely that more code is required than it would be for one type of user so the app can handle and adapt to different scenarios and use cases, which lead to new features and functionalities. When this happens, it’s reasonable to expect the performance of an app to dwindle as the codebase grows.

Code splitting is a technique where an application only loads the code it needs at the moment, and nothing more. For example, when a user navigates to a homepage, there is probably no need to load the code that powers a backend dashboard. With code splitting, we can ensure that the code for the homepage is the only code that loads, and that the cruft stays out for more optimal loading.

Code splitting is possible in a React application using React Loadable. It provides a higher-order component that can be set up to dynamically import specific components at specific times.

Component splitting

There are situations when we might want to conditionally render a component based on a user event, say when a user logs in to an account. A common way of handling this is to make use of state — the component gets rendered depending on the logged in state of the app. We call this component splitting.

Let’s see how that will look in code.

See the Pen
React-Loadable
by Kingsley Silas Chijioke (@kinsomicrote)
on CodePen.

As a basic example, say we want to conditionally render a component that contains an <h2> heading with “Hello.” Like this:

const Hello = () => { 	return ( 		<React.Fragment> 			<h2>Hello</h2> 		</React.Fragment> 	) }

We can have an openHello state in the App component with an initial value of false. Then we can have a button used to toggle the state, either display the component or hide it. We’ll throw that into a handleHello method, which looks like this:

class App extends React.Component { 	state = { 		openHello: false 	}  	handleHello = () => { 		this.setState({ openHello: !this.state.openHello }) 	} 	render() { 		return ( 			<div className="App"> 				<button onClick={this.handleHello}> 					Toggle Component 				</button>  				{ 					this.state.openHello ? 						<Hello /> 					: null 				} 			</div> 		); 	} }

Take a quick peek in DevTools and take note the Network tab:

Now, let’s refactor to make use of LoadableHello. Instead of importing the component straight up, we will do the import using Loadable. We’ll start by installing the react-loadable package:

## yarn, npm or however you roll yarn add react-loadable

Now that’s been added to our project, we need to import it into the app:

import Loadable from 'react-loadable';

We’ll use Loadable to create a “loading” component which will look like this:

const LoadableHello = Loadable({ 	loader: () => import('./Hello'), 	loading() { 		return <div>Loading...</div> 	} })

We pass a function as a value to loader which returns the Hello component we created earlier, and we make use of import() to dynamically import it. The fallback UI we want to render before the component is imported is returned by loading(). In this example, we are returning a div element, though we can also put a component in there instead if we want.

Now, instead of inputting the Hello component directly in the App component, we’ll put LoadableHello to the task so that the conditional statement will look like this:

{ 	this.state.openHello ? 		<LoadableHello /> 	: null }

Check this out — now our Hello component loads into the DOM only when the state is toggled by the button:

And that’s component splitting: the ability to load one component to load another asynchronously!

Route-based splitting

Alright, so we saw how Loadable can be used to load components via other components. Another way to go about it is us ing route-based splitting. The difference here is that components are loaded according to the current route.

So, say a user is on the homepage of an app and clicks onto a Hello view with a route of /hello. The components that belong on that route would be the only ones that load. It’s a fairly common way of handling splitting in many apps and generally works well, especially in less complex applications.

Here’s a basic example of defined routes in an app. In this case, we have two routes: (1) Home (/) and (2) Hello (/hello).

class App extends Component { 	render() { 		return ( 			<div className="App"> 				<BrowserRouter> 					<div> 						<Link to="/">Home</Link> 						<Link to="/hello">Hello</Link> 						<Switch> 							<Route exact path="/" component={Home} /> 							<Route path="/hello" component={Hello} /> 						</Switch> 					</div> 				</BrowserRouter> 			</div> 		); 	} }

As it stands, all components will render when a use switches paths, even though we want to render the one Hello component based on that path. Sure, it’s not a huge deal if we’re talking a few components, but it certainly could be as more components are added and the application grows in size.

Using Loadable, we can import only the component we want by creating a loadable component for each:

const LoadableHello = Loadable({ 	loader: () => import('./Hello'), 	loading() { 		return <div>Loading...</div> 	} }) const LoadableHome = Loadable({ 	loader: () => import('./Home'), 	loading() { 		return <div>Loading...</div> 	} }) class App extends Component { 	render() { 		return ( 			<div className="App"> 				<BrowserRouter> 					<div> 						<Link to="/">Home</Link> 						<Link to="/hello">Hello</Link> 						<Switch> 							<Route exact path="/" component={LoadableHome} /> 							<Route path="/hello" component={LoadableHello} /> 						</Switch> 					</div> 				</BrowserRouter> 			</div> 		); 	} }

Now, we serve the right code at the right time. Thanks, Loadable!

What about errors and delays?

If the imported component will load fast, there is no need to flash a “loading” component. Thankfully, Loadable has the ability to delay the loading component from showing. This is helpful to prevent it from displaying too early where it feels silly and instead show it after a notable period of time has passed where we would expect to have seen it loaded.

To do that, our sample Loadable component will look like this;

const LoadableHello = Loadable({ 	loader: () => import('./Hello'), 	loading: Loader, 	delay: 300 })

Here, we are passing the Hello component as a value to loading, which is imported via loader. By default, delay is set to 200ms, but we’ve set ours a little later to 300ms.

Now let’s add a condition to the Loader component that tells it to display the loader only after the 300ms delay we set has passed:

const Loader = (props) => { 	if (props.pastDelay) { 		return <h2>Loading...</h2> 	} else { 		return null 	} }

So the Loader component will only show if the Hello component does not show after 300ms.

react-loader also gives us an error prop which we can use to return errors that are encountered. And, because it is a prop, we can let it spit out whatever we want.

const Loader = (props) => { 	if (props.error) { 		return <div>Oh no, something went wrong!</div>; 	} else if (props.delay) { 		return <h2>Loading...</h2> 	} else { 		return null; 	} }

Note that we’re actually combining the delay and error handling together! If there’s an error off the bat, we’ll display some messaging. If there’s no error, but 300ms have passed, then we’ll show a loader. Otherwise, load up the Hello component, please!

That’s a wrap

Isn’t it great that we have more freedom and flexibility in how we load and display code these days? Code splitting — either by component or by route — is the sort of thing React was designed to do. React allows us to write modular components that contain isolated code and we can serve them whenever and wherever we want and allow them to interact with the DOM and other components. Very cool!

Hopefully this gives you a good feel for code splitting as a concept. As you get your hands dirty and start using it, it’s worth checking out more in-depth posts to get a deeper understanding of the concept.

The post Using React Loadable for Code Splitting by Components and Routes appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Writing Tests for React Applications Using Jest and Enzyme

While it is important to have a well-tested API, solid test coverage is a must for any React application. Tests increase confidence in the code and helps prevent shipping bugs to users.

That’s why we’re going to focus on testing in this post, specifically for React applications. By the end, you’ll be up and running with tests using Jest and Enzyme.

No worries if those names mean nothing to you because that’s where we’re headed right now!

Installing the test dependencies

Jest is a unit testing framework that makes testing React applications pretty darn easy because it works seamlessly with React (because, well, the Facebook team made it, though it is compatible with other JavaScript frameworks). It serves as a test runner that includes an entire library of predefined tests with the ability to mock functions as well.

Enzyme is designed to test components and it’s a great way to write assertions (or scenarios) that simulate actions that confirm the front-end UI is working correctly. In other words, it seeks out components on the front end, interacts with them, and raises a flag if any of the components aren’t working the way it’s told they should.

So, Jest and Enzyme are distinct tools, but they complement each other well.

For our purposes, we will spin up a new React project using create-react-app because it comes with Jest configured right out of the box.

yarn create react-app my-app

We still need to install enzyme and enzyme-adapter-react-16 (that number should be based on whichever version of React version you’re using).

yarn add enzyme enzyme-adapter-react-16 --dev

OK, that creates our project and gets us both Jest and Enzyme in our project in two commands. Next, we need to create a setup file for our tests. We’ll call this file setupTests.js and place it in the src folder of the project.

Here’s what should be in that file:

import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; configure({ adapter: new Adapter() });

This brings in Enzyme and sets up the adapter for running our tests.

To make things easier on us, we are going to write tests for a React application I have already built. Grab a copy of the app over on GitHub.

Taking snapshots of tests

Snapshot testing is used to keep track of changes in the app UI. If you’re wonder whether we’re dealing with literal images of the UI, the answer is no, but snapshots are super useful because they capture the code of a component at a moment in time so we can compare the component in one state versus any other possible states it might take.

The first time a test runs, a snapshot of the component code is composed and saved in a new __snapshots__ folder in the src directory. On test runs, the current UI is compared to the existing. Here’s a snapshot of a successful test of the sample project’s App component.

it("renders correctly", () => {   const wrapper = shallow(     <App />   );   expect(wrapper).toMatchSnapshot(); });

Every new snapshot that gets generated when the test suite runs will be saved in the __tests__ folder. What’s great about that Jest will check to see if the component matches is then on subsequent times when we run the test, Jest will check to see if the component matches the snapshot on subsequent tests. Here’s how that files looks.

Let’s create a conditions where the test fails. We’ll change the <h2> tag of our component from <h2>Random User</h2> to <h2>CSSTricks Tests</h2> and here’s what we get in the command line when the tests run:

If we want our change to pass the test, we either change the heading to what it was before, or we can update the snapshot file. Jest even provides instructions for how to update the snapshot right from the command line so there’s no need to update the snapshot manually:

Inspect your code changes or press `u` to update them.

So, that’s what we’ll do in this case. We press u to update the snapshot, the test passes, and we move on.

Did you catch the shallow method in our test snapshot? That’s from the Enzyme package and instructs the test to run a single component and nothing else — not even any child components that might be inside it. It’s a nice clean way to isolate code and get better information when debugging and is especially great for simple, non-interactive components.

In addition to shallow, we also have render for snapshot testing. What’s the difference, you ask? While shallow excludes child components when testing a component, render includes them while rendering to static HTML.

There is one more method in the mix to be aware of: mount. This is the most engaging type of test in the bunch because it fully renders components (like shallow and render) and their children (like render) but puts them in the DOM, which means it can fully test any component that interacts with the DOM API as well as any props that are passed to and from it. It’s a comprehensive test for interactivity. It’s also worth noting that, since it does a full mount, we’ll want to make a call to .unmount on the component after the test runs so it doesn’t conflict with other tests.

Testing Component’s Lifecycle Methods

Lifecycle methods are hooks provided by React, which get called at different stages of a component’s lifespan. These methods come in handy when handling things like API calls.
Since they are often used in React components, you can have your test suite cover them to ensure all things work as expected.

We do the fetching of data from the API when the component mounts. We can check if the lifecycle method gets called by making use of jest, which makes it possible for us to mock lifecycle methods used in React applications.

it('calls componentDidMount', () => {   jest.spyOn(App.prototype, 'componentDidMount')   const wrapper = shallow(<App />)   expect(App.prototype.componentDidMount.mock.calls.length).toBe(1) })

We attach spy to the component’s prototype, and the spy on the componentDidMount() lifecycle method of the component. Next, we assert that the lifecycle method is called once by checking for the call length.

Testing component props

How can you be sure that props from one component are being passed to another? We have a test confirm it, of course! The Enzyme API allows us to create a “mock” function so tests can simulate props being passed between components.

Let’s say we are passing user props from the main App component into a Profile component. In other words, we want the App to inform the Profile with details about user information to render a profile for that user.

First, let’s mock the user props:

const user = {   name: 'John Doe',   email: 'johndoe@gmail.com',   username: 'johndoe',   image: null }

Mock functions look a lot like other tests in that they’re wrapped around the components. However, we’re using an additional describe layer that takes the component being tested, then allows us to proceed by telling the test the expected props and values that we expect to be passed.

describe ('<Profile />', () => {   it ('contains h4', () => {     const wrapper = mount(<Profile user={user} />)     const value = wrapper.find('h4').text()     expect(value).toEqual('John Doe')   })   it ('accepts user props', () => {     const wrapper = mount(<Profile user={user} />);     expect(wrapper.props().user).toEqual(user)   }) })

This particular example contains two tests. In the first test, we pass the user props to the mounted Profile component. Then, we check to see if we can find a <h4> element that corresponds to what we have in the Profile component.

In the second test, we want to check if the props we passed to the mounted component equals the mock props we created above. Note that even though we are destructing the props in the Profile component, it does not affect the test.

Mock API calls

There’s a part in the project we’ve been using where an API call is made to fetch a list of users. And guess what? We can test that API call, too!

The slightly tricky thing about testing API calls is that we don’t actually want to hit the API. Some APIs have call limits or even costs for making making calls, so we want to avoid that. Thankfully, we can use Jest to mock axios requests. See this post for a more thorough walkthrough of using axios to make API calls.

First, we’ll create a new folder called __mock__ in the same directory where our __tests__ folder lives. This is where our mock request files will be created when the tests run.

module.exports = {   get: jest.fn(() => {     return Promise.resolve({     data: [       {         id: 1,         name: 'Jane Doe',         email: 'janedoe@gmail.com',         username: 'jdoe'       }     ]     })   }) }

We want to check and see that the GET request is made. We’ll import axios for that:

import axios from 'axios';

Just below the import statements, we need Jest to replace axios with our mock, so we add this:

jest.mock('axios')

The Jest API has a spyOn() method that takes an accessType? argument that can be used to check whether we are able to “get” data from an API call. We use jest.spyOn() to call the spied method, which we implemented in our __mock__ file, and it can be used with the shallow, render and mount tests we covered earlier.

it('fetches a list of users', () => {   const getSpy = jest.spyOn(axios, 'get')   const wrapper = shallow(     <App />   )   expect(getSpy).toBeCalled() })

We passed the test!

That’s a primer into the world of testing in a React application. Hopefully you now see the value that testing adds to a project and how relatively easy it can be to implement, thanks to the heavy lifting done by the joint powers of Jest and Enzyme.

Further reading

, , , , , ,
[Top]

Using React and XState to Build a Sign In Form

To make a sign in form with good UX requires UI state management, meaning we’d like to minimize the cognitive load to complete it and reduce the number of required user actions while making an intuitive experience. Think about it: even a relatively simple email and password sign in form needs to handle a number of different states, like empty fields, errors, password requirements, loading and success.

Thankfully, state management is what React was made for and I was able to create a sign in form with it using an approach that features XState, a JavaScript state management library using finite machines.

State management? Finite machines? We’re going to walk through these concepts together while putting together a solid sign in form.

Jumping ahead, here’s what we’re going to build together:

First, let’s set up

We’ll need a few tools before getting started. Here’s what to grab:

Once those are in hand, we can make sure our project folder is set up for development. Here’s an outline of how the files should be structured:

public/   |--src/     |--Loader/     |--SignIn/       |--contactAuthService.js       |--index.jsx       |--isPasswordShort.js       |--machineConfig.js       |--styles.js     |--globalStyles.js     |--index.jsx package.json

A little background on XState

We already mentioned that XState is a state management JavaScript library. Its approach uses finite state machines which makes it ideal for this sort of project. For example:

  • It is a thoroughly tried and tested approach to state management. Finite state machines have been around for 30+ years.
  • It is built accordance to specification.
  • It allows logic to be completely separated from implementation, making it easily testable and modular.
  • It has a visual interpreter which gives great feedback of what’s been coded and makes communicating the system to another person that much easier.

For more information on finite-state machines check out David Khourshid’s article.

Machine Config

The machine config is the core of XState. It is a statechart and it will define the logic of our form. I have broken it down into the following parts, which we’ll go over one by one.

1. The States

We need a way to control what to show, hide, enable and disable. We will control this using named-states, which include:

dataEntry: This is the state when the user can enter an email and password into the provided fields. We can consider this the default state. The current field will be highlighted in blue.

awaitingResponse: This is after the browser makes a request to the authentication service and we are waiting for the response. We’ll disable the form and replace the button with a loading indicator when the form is in this state.

emailErr: Whoops! This state is thrown when there is a problem with the email address the user has entered. We’ll highlight that field, display the error, and disable the other field and button.

passwordErr: This is another error state, this time when there is a problem with the password the user has entered. Like the previous error, we’ll highlight the field, display the error, and disable the rest of the form.

serviceErr: We reach this state when we are unable contact the authentication service, preventing the submitted data to be checked. We’ll display an error along with a “Retry” button to re-attempt a service connection.

signedIn: Success! This is when the user has successfully authenticated and proceeds past the sign in form. Normally, this would take the user to some view, but we’ll simply confirm authentication since we’re focusing solely on the form.

See the machinConfig.js file in the SignIn directory? Crack that open so we can define our states. We list them as properties of a states object. We also need to define an initial state, which mentioned earlier, will be the dataEntry state, allowing the user to enter data into the form fields.

const machineConfig = {   id: 'signIn',   initial: 'dataEntry',   states: {     dataEntry: {},     awaitingResponse: {},     emailErr: {},     passwordErr: {},     serviceErr: {},     signedIn: {},   } }  export default machineConfig

Each part of this article will show the code of machineConfig.js along with a diagram produced from the code using XState’s visualizer.

2. The Transitions

Now that we have defined our states, we need to define how to change from one state to another and, in XState, we do that with a type of event called a transition. We define transitions within each state. For example, If the ENTER_EMAIL transition is triggered when we’re in the emailErr state, then the system will move to state dataEntry.

emailErr: {   on: {     ENTER_EMAIL: {       target: 'dataEntry'     }   } }

Note that nothing would happen if a different type of transition was triggered (such as ENTER_PASSWORD) while in the emailErr state. Only transitions that are defined within the state are valid.

When a transition has no target, it is an external (by default) self-transition. When triggered, the state will exit and re-enter itself. As an example, the machine will change from dataEntry back to dataEntry when the ENTER_EMAIL transition is triggered.

Here’s how that is defined:

dataEntry: {   on: {     ENTER_EMAIL: {}   } }

Sounds weird, I know, but we’ll explain it a little later. Here’s the machineConfig.js file so far.

const machineConfig = {   id: 'signIn',   initial: 'dataEntry',   states: {     dataEntry: {       on: {         ENTER_EMAIL: {},         ENTER_PASSWORD: {},         EMAIL_BLUR: {},         PASSWORD_BLUR: {},         SUBMIT: {           target: 'awaitingResponse',         },       },     },     awaitingResponse: {},     emailErr: {       on: {         ENTER_EMAIL: {           target: 'dataEntry',         },       },     },     passwordErr: {       on: {         ENTER_PASSWORD: {           target: 'dataEntry',         },       },     },     serviceErr: {       on: {         SUBMIT: {           target: 'awaitingResponse',         },       },     },     signedIn: {},   }, };  export default machineConfig;

3. Context

We need a way to save what the user enters into the input fields. We can do that in XState with context, which is an object within the machine that enables us to store data. So, we’ll need to define that in our file as well.

Email and password are both empty strings by default. When the user enters their email or password, this is where we’ll store it.

const machineConfig = {   id: 'signIn',   context: {     email: '',     password: '',   },   ...

4. Hierarchical States

We will need a way to be more specific about our errors. Instead of simply telling the user there is an email error, we need to tell them what kind of error happened. Perhaps it’s email with the wrong format or there is no account linked to the entered email — we should let the user know so there’s no guessing. This is where we can use hierarchical states which are essentially state machines within state machines. So, instead of having a emailErr state, we can add sub-states, such as emailErr.badFormat or emailErr.noAccount.

For the emailErr state, we have defined two sub-states: badFormat and noAccount. This means the machine can no longer only be in the emailErr state; it would be either in the emailErr.badFormat state or the emailErr.noAccount state and having them parsed out allows us to provide more context to the user in the form of unique messaging in each sub-state.

const machineConfig = {   ...   states: {     ...     emailErr: {       on: {         ENTER_EMAIL: {           target: 'dataEntry',         },       },       initial: 'badFormat',       states: {         badFormat: {},         noAccount: {},       },     },     passwordErr: {       on: {         ENTER_PASSWORD: {           target: 'dataEntry',         },       },       initial: 'tooShort',       states: {         tooShort: {},         incorrect: {},       },     },     ...

5. Guards

When the user blurs an input or clicks submit, we need to check if the email and/or password are valid. If even one of those values is in a bad format, we need to prompt the user to change it. Guards allows us to transition to a state depending on those types of conditions.

Here, we’re using the EMAIL_BLUR transition to change the state to emailErr.badFormat only if the condition isBadEmailFormat returns true. We are doing a similar thing to PASSWORD_BLUR.

We’re also changing the SUBMIT transition’s value to an array of objects with a target and condition property. When the SUBMIT transition is triggered, the machine will go through each of the conditions, from first to last, and change the state of the first condition that returns true. For example, if isBadEmailFormat returns true, the machine will change to state emailErr.badFormat. However, if isBadEmailFormat returns false, the machine will move to the next condition statement and check if it returns true.

const machineConfig = {   ...   states: {     ...     dataEntry: {       ...       on: {         EMAIL_BLUR: {           cond: 'isBadEmailFormat',           target: 'emailErr.badFormat'         },         PASSWORD_BLUR: {           cond: 'isPasswordShort',           target: 'passwordErr.tooShort'         },         SUBMIT: [           {             cond: 'isBadEmailFormat',             target: 'emailErr.badFormat'           },           {             cond: 'isPasswordShort',             target: 'passwordErr.tooShort'           },           {             target: 'awaitingResponse'           }         ],       ...

6. Invoke

All of the work we’ve done so far would be for nought if we didn’t make a request to an authentication service. The result of what’s entered and submitted to the form will inform many of the states we defined. So, invoking that request should result in one of two states:

  • Transition to the signedIn state if it returns successfully, or
  • transition to one of our error states if it fails.

The invoke method allows us to declare a promise and transition to different states, depending on what that promise returns. The src property takes a function that has two parameters: context and event (but we’re only using context here). We return a promise (our authentication request) with the values of email and password from the context. If the promise returns successfully, we will transition to the state defined in the onDone property. If an error is returned, we will transition to the state defined in the onError property.

const machineConfig = {   ...   states: {     ...     // We’re in a state of waiting for a response     awaitingResponse: {       // Make a call to the authentication service             invoke: {         src: 'requestSignIn',         // If successful, move to the signedIn state         onDone: {           target: 'signedIn'         },         // If email input is unsuccessful, move to the emailErr.noAccount sub-state         onError: [           {             cond: 'isNoAccount',             target: 'emailErr.noAccount'           },           {             // If password input is unsuccessful, move to the passwordErr.incorrect sub-state             cond: 'isIncorrectPassword',             target: 'passwordErr.incorrect'           },           {             // If the service itself cannot be reached, move to the serviceErr state             cond: 'isServiceErr',             target: 'serviceErr'           }         ]       },     },     ...

7. Actions

We need a way to save what the user enters into the email and password fields. Actions enable side effects to be triggered when a transition occurs. Below, we have defined an action (cacheEmail) within the ENTER_EMAIL transition of the dataEntry state. This means if the machine is in dataEntry and the transition ENTER_EMAIL is triggered, the action cacheEmail will also be triggered.

const machineConfig = {   ...   states: {     ...     // On submit, target the two fields     dataEntry: {       on: {         ENTER_EMAIL: {           actions: 'cacheEmail'         },         ENTER_PASSWORD: {           actions: 'cachePassword'         },       },       ...     },     // If there’s an email error on that field, trigger email cache action     emailErr: {       on: {         ENTER_EMAIL: {           actions: 'cacheEmail',           ...         }       }     },     // If there’s a password error on that field, trigger password cache action     passwordErr: {       on: {         ENTER_PASSWORD: {           actions: 'cachePassword',           ...                 }       }     },     ...

8. Final State

We need to way to indicate if the user has successfully authenticated and, depending on the result, trigger the next stage of the user journey. Two things are required for this:

  • We declare that one of the states is the final state, and
  • define an onDone property that can trigger actions when that final state is reached.

Within the signedIn state, we add type: final. We also add an onDone property with action onAuthentication. Now, when the state signedIn is reached, the action onAuthentication will be triggered and the machine will be done (no longer executable).

const machineConfig = {   ...   states: {     ...     signedIn: {       type: 'final'     },     onDone: {       actions: 'onAuthentication'     },     ...

9. Test

A great feature of XState is that the machine configuration is completely independent of the actual implementation. This means we can test it now and get confidence with what we’ve made before connecting it to the UI and backend service. We can copy and paste the machine config file into XState’s visualizer and get a auto-generated statechart diagram that not only outlines all the defined states with arrows that illustrate how they’re all connected, but allows us to interact with the chart as well. This is built-in testing!

Connecting the machine to a React component

Now that we’ve written our statechart, it’s time to connect it to our UI and backend service. An XState machine options object allows us to map strings we declared in the config to functions.

We’ll begin by defining a React class component with three refs:

// SignIn/index.jsx  import React, { Component, createRef } from 'react'  class SignIn extends Component {   emailInputRef = createRef()   passwordInputRef = createRef()   submitBtnRef = createRef()      render() {     return null   } }  export default SignIn

Map out the actions

We declared the following actions in our machine config:

  • focusEmailInput
  • focusPasswordInput
  • focusSubmitBtn
  • cacheEmail
  • cachePassword
  • onAuthentication

Actions are mapped in the machine config’s actions property. Each function takes two arguments: context (ctx) and event (evt).

focusEmailInput and focusPasswordInput are pretty straightforward, however, there is a bug. These elements are being focused when coming from a disabled state. The function to focus these elements is firing right before the elements are re-enabled. The delay function gets around that.

cacheEmail and cachePassword need to update the context. To do this, we use the assign function (provided by XState). Whatever is returned by the assign function is added to our context. In our case, it is reading the input’s value from the event object and then adding that value to the context’s email or password. From there property.assign is added to the context. Again, in our case, it is reading the input’s value from the event object and adding that value to the context’s email or password property.

// SignIn/index.jsx  import { actions } from 'xstate' const { assign } = actions    const delay = func => setTimeout(() => func())  class SignIn extends Component {   ...   machineOptions = {     actions: {       focusEmailInput: () => {         delay(this.emailInputRef.current.focus())       },       focusPasswordInput: () => {         delay(this.passwordInputRef.current.focus())       },       focusSubmitBtn: () => {         delay(this.submitBtnRef.current.focus())       },       cacheEmail: assign((ctx, evt) => ({         email: evt.value       })),       cachePassword: assign((ctx, evt) => ({         password: evt.value       })),       // We’ll log a note in the console to confirm authentication       onAuthentication: () => {         console.log('user authenticated')       }     },   } }

Put up our guards

We declared the following guards in our machine config:

  • isBadEmailFormat
  • isPasswordShort
  • isNoAccount
  • isIncorrectPassword
  • isServiceErr

Guards are mapped in the machine configuration’s guards property. The isBadEmailFormat and isPasswordShort guards make use of the context to read the email and password entered by the user then pass them on to the appropriate functions. isNowAccount, isIncorrectPassword and isServiceErr make use of the event object to read what kind of error was returned from the call to the authentication service.

// isPasswordShort.js  const isPasswordShort = password => password.length < 6  export default isPasswordShort
// SignIn/index.jsx  import { isEmail } from 'validator' import isPasswordShort from './isPasswordShort'  class SignIn extends Component {   ...   machineOptions = {     ...     guards: {       isBadEmailFormat: ctx => !isEmail(ctx.email),       isPasswordShort: ctx => isPasswordShort(ctx.password),       isNoAccount: (ctx, evt) => evt.data.code === 1,       isIncorrectPassword: (ctx, evt) => evt.data.code === 2,       isServiceErr: (ctx, evt) => evt.data.code === 3     },     },   ... }

Hook up the services

We declared the following service in our machine configuration (within our invoke definition): requestSignIn.

Services are mapped in the machine configuration’s services property. In this case, the function is a promise and is passed to the email password from the context.

// contactAuthService.js // error code 1 - no account // error code 2 - wrong password // error code 3 - no response  const isSuccess = () => Math.random() >= 0.8 const generateErrCode = () => Math.floor(Math.random() * 3) + 1  const contactAuthService = (email, password) =>   new Promise((resolve, reject) => {     console.log(`email: $ {email}`)     console.log(`password: $ {password}`)     setTimeout(() => {       if (isSuccess()) resolve()       reject({ code: generateErrCode() })     }, 1500) })  export default contactAuthService
// SignIn/index.jsx ... import contactAuthService from './contactAuthService.js'  class SignIn extends Component {   ...   machineOptions = {     ...     services: {       requestSignIn: ctx => contactAuthService(ctx.email, ctx.password)     }   },   ... }

react-xstate-js connects React and XState

Now that we have our machine config and options at the ready, we can create the actual machine! In order to use XState in a real world scenario, that requires an interpreter. react-xstate-js is an interpreter that connects React with XState using the render props approach. (Full disclosure, I developed this library.) It takes two props — config and options — and returns an XState service and state object.

// SignIn/index.jsx ... import { Machine } from 'react-xstate-js' import machineConfig from './machineConfig'  class SignIn extends Component {   ...   render() {     <Machine config={machineConfig} options={this.machineOptions}>       {({ service, state }) => null}     </Machine>   } }

Let’s make the UI!

OK, we have a functional machine but the user needs to see the form in order to use it. That means it’s time to create the markup for the UI component. There are two things we need to do to communicate with our machine:

1. Read the state

To determine what state we are in, we can use the state’s matches method and return a boolean. For example: state.matches('dataEntry').

2. Fire a transition

To fire a transition, we use the service’s send method. It takes an object with the transitions type we want to trigger as well as any other key and value pairs we want in the evt object. For example: service.send({ type: 'SUBMIT' }).

// SignIn/index.jsx  ... import {   Form,   H1,   Label,   Recede,   Input,   ErrMsg,   Button,   Authenticated,   MetaWrapper,   Pre } from './styles'  class SignIn extends Component {   ...   render() {     <Machine config={machineConfig} options={this.machineOptions}>       {({ service, state }) => {         const disableEmail =           state.matches('passwordErr') ||           state.matches('awaitingResponse') ||           state.matches('serviceErr')                    const disablePassword =           state.matches('emailErr') ||           state.matches('awaitingResponse') ||           state.matches('serviceErr')                  const disableSubmit =           state.matches('emailErr') ||           state.matches('passwordErr') ||           state.matches('awaitingResponse')                  const fadeHeading =           state.matches('emailErr') ||           state.matches('passwordErr') ||           state.matches('awaitingResponse') ||           state.matches('serviceErr')          return (           <Form             onSubmit={e => {               e.preventDefault()               service.send({ type: 'SUBMIT' })             }}             noValidate           >             <H1 fade={fadeHeading}>Welcome Back</H1>              <Label htmlFor="email" disabled={disableEmail}>               email             </Label>             <Input               id="email"               type="email"               placeholder="charlie@gmail.com"               onBlur={() => {                 service.send({ type: 'EMAIL_BLUR' })               }}               value={state.context.email}               err={state.matches('emailErr')}               disabled={disableEmail}               onChange={e => {                 service.send({                   type: 'ENTER_EMAIL',                   value: e.target.value                 })               }}               ref={this.emailInputRef}               autoFocus             />             <ErrMsg>               {state.matches({ emailErr: 'badFormat' }) &&                 "email format doesn't look right"}               {state.matches({ emailErr: 'noAccount' }) &&                 'no account linked with this email'}             </ErrMsg>                          <Label htmlFor="password" disabled={disablePassword}>               password <Recede>(min. 6 characters)</Recede>             </Label>             <Input               id="password"               type="password"               placeholder="Passw0rd!"               value={state.context.password}               err={state.matches('passwordErr')}               disabled={disablePassword}               onBlur={() => {                 service.send({ type: 'PASSWORD_BLUR' })               }}               onChange={e => {                 service.send({                   type: 'ENTER_PASSWORD',                   value: e.target.value                 })               }}               ref={this.passwordInputRef}             />             <ErrMsg>               {state.matches({ passwordErr: 'tooShort' }) &&                 'password too short (min. 6 characters)'}               {state.matches({ passwordErr: 'incorrect' }) &&                 'incorrect password'}             </ErrMsg>                          <Button               type="submit"               disabled={disableSubmit}               loading={state.matches('awaitingResponse')}               ref={this.submitBtnRef}             >               {state.matches('awaitingResponse') && (                 <>                   loading                   <Loader />                 </>               )}               {state.matches('serviceErr') && 'retry'}               {!state.matches('awaitingResponse') &&                 !state.matches('serviceErr') &&                 'sign in'               }             </Button>             <ErrMsg>               {state.matches('serviceErr') && 'problem contacting server'}             </ErrMsg>              {state.matches('signedIn') && (               <Authenticated>                 <H1>authenticated</H1>               </Authenticated>             )}           </Form>         )       }}     </Machine>   } }

We have a form!

And there you have it. A sign in form that has a great user experience controlled by XState. Not only were we able to create a form a user can interact with, we also put a lot of thought into the many states and types of interactions that’s need to be considered, which is a good exercise for any piece of functionality that would go into a component.

Hit up the comments form if there’s something that doesn’t make sense or if there’s something else you think might need to be considered in the form. Would love to hear your thoughts!

More resources

The post Using React and XState to Build a Sign In Form appeared first on CSS-Tricks.

CSS-Tricks

, , , , ,
[Top]

React 16.6.0 Goodies

React 16.6.0 was released October 2018 and with it came goodies that spice up the way we can develop with React. We’re going to cover what I consider the best of those new goodies with examples of how we can put them to use in our work.

React.memo() avoids unnecessary re-rendering

There are situations where a component re-renders, even if neither its state nor its props changed. That adds up and can be an expensive operation.

Here’s an example of a counter to show what we’re talking about:

See the Pen
React counter w/o React.memo()
by CSS-Tricks (@css-tricks)
on CodePen.

We have a child component that receives a specific value as props that do not change.

const Child = props => {   console.log("rendered");   return <React.Fragment>{props.name}</React.Fragment>; }

The child’s value is determined by the state of the App component. It’s state doesn’t change. It’s props remain the same.

class App extends React.Component {   state = {     count: 1,     name: "Jioke"   };    handleClick = () => {     this.setState({       count: this.state.count + 1     });   };    render() {     return (       <React.Fragment>         <Child name={this.state.name} />         <div>{this.state.count}</div>         <button onClick={this.handleClick}>+</button>       </React.Fragment>     );   } }

Yet, each button click results in two things happening: the value of count is incremented and the child component is re-rendered. Just watch:

We could resolve this with a class component using the shouldComponentUpdate() lifecycle hook, which would look like this:

class Child extends React.Component {      // No re-render, please!   shouldComponentUpdate(nextProps, nextState) {     return nextProps.name != this.props.name   }      render() {     console.log('rendered')     return <React.Fragment>{this.props.name}</React.Fragment>   } }

That’s where React.memo() comes into play. It’s a higher-order component we can wrap around the child and, presto, now the child is shielded from unnecessary additional rendering.

const Child = React.memo(props => {   console.log("rendered");   return <React.Fragment>{props.name}</React.Fragment>; });

See the Pen
React.memo 2
by CSS-Tricks (@css-tricks)
on CodePen.

React.lazy() makes importing files a breeze while Suspense provides a fallback UI

Code splitting is crucial in web development—it enables us to import only the files we, which is not only reduces an application’s initial load, but is a core principle of the React framework.

Well, React now enables code splitting using React.lazy() and suspense right at the component level.

By default, if making use of a component (even if its usage depends on a condition), then we import it into the file where you will be using it. React.lazy() can now handle the importation like this:

const MyCounter = lazy(() => import("./Counter"));

This single line returns a promise that resolves to the imported component. From here, we can use the component as we normally would.

const App = () => (   <div>     <MyCounter />   </div> );

There are cases where we might want to render a fallback UI before the component is ready to render. For example, it might take a moment for an API call to fetch and return data. This is a great opportunity to show a loading state while the user waits. Suspense can do just that.

// Using React.lazy() to import the Counter component const MyCounter = lazy(() => import("./Counter")); const App = () => (   <div>     // Using Suspense to render a loading state while we wait for the Counter     <Suspense fallback={<div>Loading...</div>}>       <MyCounter />     </Suspense>   </div> );

Suspense’s fallback prop can accept a React element, so go nuts. It can be used to display whatever fallback UI we want while the component loads.

contextType accesses provider context and passes state without render props

The Context API made it possible to share state among multiple components without having to make use of a third-party library.

Well, React 16.6 makes it possible to declare contextType in a component to access the context from a provider. This saves us from having to make use of render props to pass down context to the consumer.

See the Pen
React contextType
by CSS-Tricks (@css-tricks)
on CodePen.

First, let’s create our context:

const UserContext = React.createContext({});  const UserProvider = UserContext.Provider; const UserConsumer = UserContext.Consumer;

We’ll make use of the provider in the App component:

class App extends React.Component {   state = {     input: "",     name: 'John Doe'   };    handleInputChange = event => {     event.preventDefault();     this.setState({ input: event.target.value });   };    handleSubmit = event => {     event.preventDefault();     this.setState({ name: this.state.input, input: '' })   };   render() {     return (       <div>         <UserProvider           value={{             state: this.state,             actions: {               handleSubmit: this.handleSubmit,               handleInputChange: this.handleInputChange             }           }}         >           <User />         </UserProvider>       </div>     );   } }

The provider passes the state and the methods to consumer components that will make use of them via the value prop. To access the context, we’ll make use of this.context instead of making render props like we normally would.

class User extends React.Component {   static contextType = UserContext;   render() {     const { state, actions } = this.context;     return (       <div>         <div>           <h2>Hello, {state.name}!</h2>         </div>         <div>           <div>             <input               type="text"               value={state.input}               placeholder="Name"               onChange={actions.handleInputChange}             />           </div>           <div>             <button onClick={actions.handleSubmit}>Submit</button>           </div>         </div>       </div>     );   } }

We set static contextType to UserContext which we created earlier. With that, we are able to extract the context which includes the state and methods from this.context. We make use of ES6 destructuring to get the values so we can make use of them in the User component, which is the consumer. This looks so much cleaner and is easier to read compared to doing this with render props.

getDerivedStateFromErrors()

We have error boundary to handle errors, which makes use of componentDidCatch() and that gets fired after the DOM has been updated. It’s well suited for error reporting. But now we have getDerivedStateFromErrors() to render a fallback UI before the render completes if an error is caught. Sort of the same concept as Suspense, but for error states instead of loading states.

See the Pen
React getDerivedStateFromError
by CSS-Tricks (@css-tricks)
on CodePen.

Let’s create our error boundary component to capture the moment something goes awry:

class ErrorBoundary extends React.Component {   constructor(props) {     super(props);     this.state = {       hasError: false     };   }      // If hasError is true, then trigger the fallback UI   static getDerivedStateFromError(error) {     return { hasError: true };   }      // The fallback UI   render() {     if (this.state.hasError) {       return (         <h1>Oops, something went wrong :(</h1>       );     }     return this.props.children;   } }

We make use of getDerivedStateFromError() to spot that an error was caught by the error boundary and then return hasError as true when an error occurs. When this happens, we want to display a message to inform the user that an error has encountered.

class Counter extends React.Component {   state = {     count: 1   }    handleClick = () => {     this.setState({       count: this.state.count + 1     })   }    // If the count is greater than 5, throw an error   render() {     if (this.state.count > 5) {       throw new Error('Error')     }     return (       <div>         <h2>{this.state.count}</h2>         <button onClick={this.handleClick}>+</button>       </div>     )   } }

That’s going to trigger an error when the value of count is greater than five. Next, we need to wrap our Counter component as a child of ErrorBoundary component to apply the error conditions to the component:

const App = () => (   <div>     // Wrap the component in the ErrorBoundary to attach the error conditions and UI     <ErrorBoundary>       <Counter />     </ErrorBoundary>   </div> )

We can even limit the error to the specific piece that is broken. So, for example, let’s take a listing of locations. Instead swapping the entire list of locations for the error UI, we can slap it at the specific location where the error happened.

See the Pen
React getDerivedStateFromError 1
by Kingsley Silas Chijioke (@kinsomicrote)
on CodePen.

Pretty nice, right?

React continues to add a bunch of useful features while making it easier to write code with each release and v16.6 is no exception. If you’ve already started using any of the latest goodies that shipped in this release, please let me—I’d be interested in seeing how you’re using them in a real project.

More Information

The post React 16.6.0 Goodies appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

Intro to React Hooks

Hooks make it possible to organize logic in components, making them tiny and reusable without writing a class. In a sense, they’re React’s way of leaning into functions because, before them, we’d have to write them in a component and, while components have proven to be powerful and functional in and of themselves, they have to render something on the front end. That’s all fine and dandy to some extent, but the result is a DOM that is littered with divs that make it gnarly to dig through through DevTools and debug.

Well, React Hooks change that. Instead of relying on the top-down flow of components or abstracting components in various ways, like higher-order components, we can call and manage flow inside of a component. Dan Abramov explains it well in his Making Sense of React post:

Hooks apply the React philosophy (explicit data flow and composition) inside a component, rather than just between the components. That’s why I feel that Hooks are a natural fit for the React component model.

Unlike patterns like render props or higher-order components, Hooks don’t introduce unnecessary nesting into your component tree. They also don’t suffer from the drawbacks of mixins.

The rest of Dan’s post provides a lot of useful context for why the React team is moving in this direction (they’re now available in React v16.7.0-alpha) and the various problems that hooks are designed to solve. The React docs have an introduction to hooks that, in turn, contains a section on what motivated the team to make them. We’re more concerned with how the heck to use them, so let’s move on to some examples!

The important thing to note as we get started is that there are nine hooks currently available, but we’re going to look at what the React docs call the three basic ones: useState(), useEffect, and setContext(). We’ll dig into each one in this post with a summary of the advanced hooks at the end.

Defining state with useState()

If you’ve worked with React at any level, then you’re probably familiar with how state is generally defined: write a class and use this.state to initialize a class:

class SomeComponent extends React.component {   constructor(props)   super(props);   this.state = {     name: Barney Stinson // Some property with the default state value       } }

React hooks allow us to scrap all that class stuff and put the useState() hook to use instead. Something like this:

import { useState } from 'react';      function SomeComponent() {   const [name, setName] = useState('Barney Stinson'); // Defines state variable (name) and call (setName) -- both of which can be named anything }

Say what?! That’s it! Notice that we’re working outside of a class. Hooks don’t work inside of a class because they’re used in place of them. We’re using the hook directly in the component:

import { useState } from 'react';      function SomeComponent() {   const [name, setName] = useState('Barney Stinson');      return     <div>       <p>Howdy, {name}</p>     </div> }

Oh, you want to update the state of name? Let’s add an input and submit button to the output and call setName to update the default name on submission.

import { useState } from 'react'      function SomeComponent() {   const [input, setValue] = useState("");   const [name, setName] = useState('Barney Stinson');      handleInput = (event) => {     setValue(event.target.value);   }      updateName = (event) => {     event.preventDefault();     setName(input);     setValue("");   }      return (     <div>       <p>Hello, {name}!</p>       <div>         <input type="text" value={input} onChange={handleInput} />         <button onClick={updateName}>Save</button>       </div>     </div>   ) }

See the Pen React Hook: setState Example by Geoff Graham (@geoffgraham) on CodePen.

Notice something else in this example? We’re constructing two different states (input and name). That’s because the useState() hook allows managing multiple states in the same component! In this case, input is the property and setValue holds the state of the input element, which is called by the handleInput function then triggers the updateName function that takes the input value and sets it as the new name state.

Create side effects with useEffect()

So, defining and setting states is all fine and dandy, but there’s another hook called useEffect() that can be used to—you guessed it—define and reuse effects directly in a component without the need for a class or the need to use both redundant code for each lifecycle of a method (i.e. componentDidMount, componentDidUpdate, and componentWillUnmount).

When we talk about effects, we’re referring to things like API calls, updates to the DOM, and event listeners, among other things. The React documentation cites examples like data fetching, setting up subscriptions, and changing the DOM as possible use cases for this hook. Perhaps the biggest differentiator from setState() is that useEffect() runs after render. Think of it like giving React an instruction to hold onto the function that passes and then make adjustments to the DOM after the render has happened plus any updates after that. Again, the React documentation spells it out nicely:

By default, it runs both after the first render and after every update. […] Instead of thinking in terms of “mounting” and “updating”, you might find it easier to think that effects happen “after render”. React guarantees the DOM has been updated by the time it runs the effects.

Right on, so how do we run these effects? Well, we start off by importing the hook the way we did for setState().

import { useEffect } from 'react';

In fact, we can call both setState() and useEffect() in the same import:

import { useState, useEffect } from 'react';

Or, construct them:

const { useState, useEffect } = React;

So, let’s deviate from our previous name example by hooking into an external API that contains user data using axios inside the useEffect() hook then renders that data into a list of of users.

First, let’s bring in our hooks and initialize the App.

const { useState, useEffect } = React  const App = () => {   // Hooks and render UI }

Now, let’s put setState() to define users as a variable that contains a state of setUsers that we’ll pass the user data to once it has been fetched so that it’s ready for render.

const { useState, useEffect } = React  const App = () => {   const [users, setUsers] = useState([]);   // Our effects come next }

Here’s where useEffect() comes into play. We’re going to use it to connect to an API and fetch data from it, then map that data to variables we can call on render.

const { useState, useEffect } = React  const App = () => {   const [users, setUsers] = useState([]);      useEffect(() => {     // Connect to the Random User API using axios     axios("https://randomuser.me/api/?results=10")       // Once we get a response, fetch name, username, email and image data       // and map them to defined variables we can use later.       .then(response =>         response.data.results.map(user => ({           name: `{user.name.first} $  {user.name.last}`,           username: `{user.login.username}`,           email: `{user.email}`,           image: `{user.picture.thumbnail}`         }))       )       // Finally, update the `setUsers` state with the fetched data       // so it stores it for use on render       .then(data => {         setUsers(data);       });   }, []);      // The UI to render }

OK, now let’s render our component!

const { useState, useEffect } = React  const App = () => {   const [users, setUsers] = useState([]);    useEffect(() => {     axios("https://randomuser.me/api/?results=10")       .then(response =>         response.data.results.map(user => ({           name: `{user.name.first} $  {user.name.last}`,           username: `{user.login.username}`,           email: `{user.email}`,           image: `{user.picture.thumbnail}`         }))       )       .then(data => {         setUsers(data);       });   }, []);      return (     <div className="users">       {users.map(user => (         <div key={user.username} className="users__user">           <img src={user.image} className="users__avatar" />           <div className="users__meta">             <h1>{user.name}</h1>             <p>{user.email}</p>           </div>         </div>       ))}     </div>   ) }

Here’s what that gets us:

See the Pen React Hook: setEffect example by Geoff Graham (@geoffgraham) on CodePen.

It’s worth noting that useEffect() is capable of so, so, so much more, like chaining effects and triggering them on condition. Plus, there are cases where we need to cleanup after an effect has run—like subscribing to an external resource—to prevent memory leaks. Totally worth running through the detailed explanation of effects with cleanup in the React documentation.

Context and useContext()

Context in React makes it possible to pass props down from a parent component to a child component. This saves you from the hassle of prop drilling. However, you could only make use of context in class components, but now you can make use of context in functional components using useContext() . Let’s create a counter example, we will pass the state and functions which will be used to increase or decrease the count from the parent component to child component using useContext(). First, let’s create our context:

const CountContext = React.createContext();

We’ll declare the count state and increase/decrease methods of our counter in our App component and set up the wrapper that will hold the component. We’ll put the context hook to use in the actual counter component in just a bit.

const App = () => {   // Use `setState()` to define a count variable and its state   const [count, setCount] = useState(0);      // Construct a method that increases the current `setCount` variable state by 1 with each click   const increase = () => {     setCount(count + 1);   };      // Construct a method that decreases the current `setCount` variable state by 1 with each click.   const decrease = () => {     setCount(count - 1);   };    // Create a wrapper for the counter component that contains the provider that will supply the context value.   return (     <div>       <CountContext.Provider         // The value is takes the count value and updates when either the increase or decrease methods are triggered.         value={{ count, increase, decrease }}       >         // Call the Counter component we will create next         <Counter />       </CountContext.Provider>     </div>   ); };

Alright, onto the Counter component! useContext() accepts an object (we’re passing in the CountContext provider) and allows us to tell React exactly what value we want (`count) and what methods trigger updated values (increase and decrease). Then, of course, we’ll round things out by rendering the component, which is called by the App.

const Counter = () => {   const { count, increase, decrease } = useContext(CountContext);   return (     <div className="counter">       <button onClick={decrease}>-</button>       <span className="count">{count}</span>       <button onClick={increase}>+</button>     </div>   ); };

And voilà! Behold our mighty counter with the count powered by context objects and values.

See the Pen React hooks – useContext by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Wrapping up

We’ve merely scratched the surface of what React hooks are capable of doing, but hopefully this gives you a solid foundation. For example, there are even more advanced hooks that are available in addition to the basic ones we covered in this post. Here’s a list of those hooks with the descriptions offered by the documentation so you can level up now that you’re equipped with the basics:

Hook Description
userReducer() An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method.
useCallback() Returns a memoized callback. Pass an inline callback and an array of inputs. useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.
useMemo() Returns a memoized value. Pass a “create” function and an array of inputs. useMemo will only recompute the memoized value when one of the inputs has changed.
useRef() useRef returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
useImperativeMethods useImperativeMethods customizes the instance value that is exposed to parent components when using ref. As always, imperative code using refs should be avoided in most cases. useImperativeMethods should be used with forwardRef.
useLayoutEffect The signature is identical to useEffect, but it fires synchronously after all DOM mutations. Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside useLayoutEffect will be flushed synchronously, before the browser has a chance to paint.

The post Intro to React Hooks appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]