Tag: React

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

, ,

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]

Using React Portals to Render Children Outside the DOM Hierarchy

Say we need to render a child element into a React application. Easy right? That child is mounted to the nearest DOM element and rendered inside of it as a result.

render() {   return (     <div>       // Child to render inside of the div     </div>   ); }

But! What if we want to render that child outside of the div somewhere else? That could be tricky because it breaks the convention that a component needs to render as a new element and follow a parent-child hierarchy. The parent wants to go where its child goes.

That’s where React Portals come in. They provide a way to render elements outside the DOM hierarchy so that elements are a little more portable. It may not be a perfect analogy, but Portals are sort of like the pipes in Mario Bros. that transport you from the normal flow of the game and into a different region.

The cool thing about Portals? Even though they trigger their own events that are independent of the child’s parent element, the parent is still listening to those events, which can be useful for passing events across an app.

We’re going to create a Portal together in this post then make it into a re-usable component. Let’s go!

The example we’re building

Here’s a relatively simple example of a Portal in action:

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

Toggling an element’s visibility is nothing new. But, if you look at the code carefully, you’ll notice that the outputted element is controlled by the button even though it is not a direct descendent of it. In fact, if you compare the source code to the rendered output in DevTools, you’ll see the relationship:

So the outputted element’s parent actually listens for the button click event and allows the child to be inserted even though it and the button are separate siblings in the DOM. Let’s break down the steps for creating this toggled Portal element to see how it all works.

Step 1: Create the Portal element

The first line of a React application will tell you that an App element is rendered on the document root using ReactDOM. Like this;

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

We need to place the App element in an HTML file to execute it:

<div id="App"></div>

Same sort of thing with Portals. First thing to creating a Portal is to create a new div element in the HTML file.

<div id="portal"></div>

This div will serve as our target. We’re using #portal as the ID, but it doesn’t have to be that. Any component that gets rendered inside this target div will maintain React’s context. We need to store the div as the value of a variable so we can make use of the Portal component that we’ll create:

const portalRoot = document.getElementById("portal");

Looks a lot like the method to execute the App element, right?

Step 2: Create a Portal component

Next, let’s set up the Portal as a component:

class Portal extends React.Component {   constructor() {     super();     // 1: Create a new div that wraps the component     this.el = document.createElement("div");   }   // 2: Append the element to the DOM when it mounts   componentDidMount = () => {     portalRoot.appendChild(this.el);   };   // 3: Remove the element when it unmounts   componentWillUnmount = () => {     portalRoot.removeChild(this.el);   };   render() {     // 4: Render the element's children in a Portal     const { children } = this.props;     return ReactDOM.createPortal(children, this.el);   } }

Let’s step back and take a look at what is happening here.

We create a new div element in the constructor and set it as a value to this.el. When the Portal component mounts, this.el is appended as a child to that div in the HTML file where we added it. That’s the <div id="portal"></div> line in our case.

The DOM tree will look like this.

<div> // Portal, which is also portalRoot   <div> // this.el   </div> </div>

If you’re new to React and are confused by the concept of mounting and unmounting an element, Jake Trent has a good explanation. TL;DR: Mounting is the moment the element is inserted into the DOM.

When the component unmounts we want to remove the child to avoid any memory leakage. We will import this Portal component into another component where it gets used, which is the the div that contains the header and button in our example. In doing so, we’ll pass the children elements of the Portal component along with it. This is why we have this.props.children.

Step 3: Using the Portal

To render the Portal component’s children, we make use of ReactDOM.createPortal(). This is a special ReactDOM method that accepts the children and the element we created. To see how the Portal works, let’s make use of it in our App component.

But, before we do that, let’s cover the basics of how we want the App to function. When the App loads, we want to display a text and a button — we can then toggle the button to either show or hide the Portal component.

class App extends React.Component {   // The initial toggle state is false so the Portal element is out of view   state = {     on: false   };    toggle = () => {     // Create a new "on" state to mount the Portal component via the button     this.setState({       on: !this.state.on     });   };   // Now, let's render the components   render() {     const { on } = this.state;     return (       // The div where that uses the Portal component child       <div>         <header>           <h1>Welcome to React</h1>         </header>         <React.Fragment>           // The button that toggles the Portal component state           // The Portal parent is listening for the event           <button onClick={this.toggle}>Toggle Portal</button>           // Mount or unmount the Portal on button click           <Portal>             {               on ?                 <h1>This is a portal!</h1>               : null             }           </Portal>         </React.Fragment>       </div>     );   } }

Since we want to toggle the Portal on and off, we need to make use of component state to manage the toggling. That’s basically a method to set a state of on to either true or false on the click event. The portal gets rendered when on is true; else we render nothing.

This is how the DOM looks like when the on state is set to true.

When on is false, the Portal component is not being rendered in the root, so the DOM looks like this.

More use cases

Modals are a perfect candidate for Portals. In fact, the React docs use it as the primary example for how Portals work:

See the Pen Example: Portals by Dan Abramov (@gaearon) on CodePen.

It’s the same concept, where a Portal component is created and a state is used to append the its child elements to the Modal component.

We can even insert data from an outside source into a modal. In this example, the App component lists users fetched from an API using axios.

See the Pen React Portal 3 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

How about tooltips? David Gilberston has a nice demo:

See the Pen React Portal Tooptip by David Gilbertson (@davidgilbertson) on CodePen.

J Scott Smith shows how Portals can be used to escape positioning:

He has another slick example that demonstrates inserting elements and managing state:

Summary

That’s a wrap! Hopefully this gives you a solid base understanding of Portals as far as what they are, what they do, and how to use them in a React application. The concept may seem trivial, but having the ability to move elements outside of the DOM hierarchy is a handy way to make components a little more extensible and re-usable… all of which points to the core benefits of using React in the first place.

More information

The post Using React Portals to Render Children Outside the DOM Hierarchy appeared first on CSS-Tricks.

CSS-Tricks

, , , , , ,
[Top]

Animating Between Views in React

You know how some sites and web apps have that neat native feel when transitioning between two pages or views? Sarah Drasner has shown some good examples and even a Vue library to boot.

These animations are the type of features that can turn a good user experience into a great one. But to achieve this in a React stack, it is necessary to couple crucial parts in your application: the routing logic and the animation tooling.

Let’s start with animations. We’ll be building with React, and there are great options out there for us to leverage. Notably, the react-transition-group is the official package that handles elements entering and leaving the DOM. Let’s explore some relatively straightforward patterns we can apply, even to existing components.

Transitions using react-transition-group

First, let’s get familiar with the react-transition-group library to examine how we can use it for elements entering and leaving the DOM.

Single components transitions

As a simple example of a use case, we can try to animate a modal or dialog — you know, the type of element that benefits from animations that allow it enter and leave smoothly.

A dialog component might look something like this:

import React from "react";  class Dialog extends React.Component {   render() {     const { isOpen, onClose, message } = this.props;     return (       isOpen && (         <div className="dialog--overlay" onClick={onClose}>           <div className="dialog">{message}</div>         </div>       )     );   } }

Notice we are using the isOpen prop to determine whether the component is rendered or not. Thanks to the simplicity of the recently modified API provided by react-transition-group module, we can add a CSS-based transition to this component without much overhead.

First thing we need is to wrap the entire component in another TransitionGroup component. Inside, we keep the prop to mount or unmount the dialog, which we are wrapping in a CSSTransition.

import React from "react"; import { TransitionGroup, CSSTransition } from "react-transition-group";  class Dialog extends React.Component {   render() {     const { isOpen, onClose, message } = this.props;     return (       <TransitionGroup component={null}>         {isOpen && (           <CSSTransition classNames="dialog" timeout={300}>             <div className="dialog--overlay" onClick={onClose}>               <div className="dialog">{message}</div>             </div>           </CSSTransition>         )}       </TransitionGroup>     );   } }

Every time isOpen is modified, a sequence of class names changes will happen in the dialog’s root element.

If we set the classNames prop to "fade", then fade-enter will be added immediately before the element mounts and then fade-enter-active when the transition kicks off. We should see fade-enter-done when the transition finishes, based on the timeout that was set. Exactly the same will happen with the exit class name group at the time the element is about to unmount.

This way, we can simply define a set of CSS rules to declare our transitions.

.dialog-enter {   opacity: 0.01;   transform: scale(1.1); }  .dialog-enter-active {   opacity: 1;   transform: scale(1);   transition: all 300ms; }  .dialog-exit {   opacity: 1;   transform: scale(1); }  .dialog-exit-active {   opacity: 0.01;   transform: scale(1.1);   transition: all 300ms; }

JavaScript Transitions

If we want to orchestrate more complex animations using a JavaScript library, then we can use the Transition component instead.

This component doesn’t do anything for us like the CSSTransition did, but it does expose hooks on each transition cycle. We can pass methods to each hook to run calculations and animations.

<TransitionGroup component={null}>   {isOpen && (     <Transition       onEnter={node => animateOnEnter(node)}       onExit={node => animateOnExit(node)}       timeout={300}     >       <div className="dialog--overlay" onClick={onClose}>         <div className="dialog">{message}</div>       </div>     </Transition>   )} </TransitionGroup>

Each hook passes the node to the callback as a first argument — this gives control for any mutation we want when the element mounts or unmounts.

Routing

The React ecosystem offers plenty of router options. I’m gonna use react-router-dom since it’s the most popular choice and because most React developers are familiar with the syntax.

Let’s start with a basic route definition:

import React, { Component } from 'react' import { BrowserRouter, Switch, Route } from 'react-router-dom' import Home from '../views/Home' import Author from '../views/Author' import About from '../views/About' import Nav from '../components/Nav'  class App extends Component {   render() {     return (       <BrowserRouter>         <div className="app">           <Switch>             <Route exact path="/" component={Home}/>             <Route path="/author" component={Author} />             <Route path="/about" component={About} />           </Switch>         </div>       </BrowserRouter>     )   } }

We want three routes in this application: home, author and about.

The BrowserRouter component handles the browser’s history updates, while Switch decides which Route element to render depending on the path prop. Here’s that without any transitions:

Don’t worry, we’ll be adding in page transitions as we go.

Oil and water

While both react-transition-group and react-router-dom are great and handy packages for their intended uses, mixing them together can break their functionality.

For example, the Switch component in react-router-dom expects direct Route children and the TransitionGroup components in react-transition-group expect CSSTransition or Transition components to be direct children of it too. So, we’re unable to wrap them the way we did earlier.

We also cannot toggle views with the same boolean approach as before since it’s handled internally by the react-router-dom logic.

React keys to the rescue

Although the solution might not be as clean as our previous examples, it is possible to use the libraries together. The first thing we need to do is to move our routes declaration to a render prop.

<BrowserRouter>   <div className="app">     <Route render={(location) => {       return (         <Switch location={location}>           <Route exact path="/" component={Home}/>           <Route path="/author" component={Author} />           <Route path="/about" component={About} />         </Switch>       )}     /> </BrowserRouter>

Nothing has changed as far as functionality. The difference is that we are now in control of what gets rendered every time the location in the browser changes.

Also, react-router-dom provides a unique key in the location object every time this happens.

In case you are not familiar with them, React keys identify elements in the virtual DOM tree. Most times, we don’t need to indicate them since React will detect which part of the DOM should change and then patch it.

<Route render={({ location }) => {   const { pathname, key } = location    return (     <TransitionGroup component={null}>       <Transition         key={key}         appear={true}         onEnter={(node, appears) => play(pathname, node, appears)}         timeout={{enter: 750, exit: 0}}       >         <Switch location={location}>           <Route exact path="/" component={Home}/>           <Route path="/author" component={Author} />           <Route path="/about" component={About} />         </Switch>       </Transition>     </TransitionGroup>   ) }}/>

Constantly changing the key of an element — even when its children or props haven’t been modified — will force React to remove it from the DOM and remount it. This helps us emulate the boolean toggle approach we had before and it’s important for us here because we can place a single Transition element and reuse it for all of our view transitions, allowing us to mix routing and transition components.

Inside the animation function

Once the transition hooks are called on each location change, we can run a method and use any animation library to build more complex scenes for our transitions.

export const play = (pathname, node, appears) => {   const delay = appears ? 0 : 0.5   let timeline    if (pathname === '/')     timeline = getHomeTimeline(node, delay)   else     timeline = getDefaultTimeline(node, delay)    timeline.play() }

Our play function will build a GreenSock timeline here depending on the pathname, and we can set as many transitions as we want for each different routes.

Once the timeline is built for the current pathname, we play it.

const getHomeTimeline = (node, delay) => {   const timeline = new Timeline({ paused: true });   const texts = node.querySelectorAll('h1 > div');    timeline     .from(node, 0, { display: 'none', autoAlpha: 0, delay })     .staggerFrom(texts, 0.375, { autoAlpha: 0, x: -25, ease: Power1.easeOut }, 0.125);    return timeline }

Each timeline method digs into the DOM nodes of the view and animates them. You can use other animation libraries instead of GreenSock, but the important detail is that we build the timeline beforehand so that our main play method can decide which one should run for each route.

Success!

I’ve used this approach on lots of projects, and though it doesn’t present obvious performance issues for inner navigations, I did notice a concurrency issue between the browser’s initial DOM tree build and the first route animation. This caused a visual lag on the animation for the first load of the application.

To make sure animations are smooth in each stage of the application, there’s one last thing we can do.

Profiling the initial load

Here’s what I found when auditing the application in Chrome DevTools after a hard refresh:

You can see two lines: one blue and one red. Blue represents the load event and red the DOMContentLoaded. Both intersect the execution of the initial animations.

These lines are indicating that elements are animating while the browser hasn’t yet finished building the entire DOM tree or it’s parsing resources. Animations account for big performance hits. If we want anything else to happen, we’d have to wait for the browser to be ready with these heavy and important tasks before running our transitions.

After trying a lot of different approaches, the solution that actually worked was to move the animation after these events — simple as that. The issue is that we can’t rely on event listeners.

window.addEventListener(‘DOMContentLoaded’, () => {   timeline.play() })

If for some reason, the event occurs before we declare the listener, the callback we pass will never run and this could lead to our animations never happening and an empty view.

Since this is a concurrency and asynchronous issue, I decided to rely on promises, but then the question became: how can promises and event listeners be used together?

By creating a promise that gets resolved when the event takes place. That’s how.

window.loadPromise = new Promise(resolve => {   window.addEventListener(‘DOMContentLoaded’, resolve) })

We can put this in the document head or just before the script tag that loads the application bundle. This will make sure the event never happens before the Promise is created.

Plus, doing this allows us to use the globally exposed loadPromise to any animation in our application. Let’s say that we don’t only want to animate the entry view but a cookie banner or the header of the application. We can simply call each of these animations after the promise has resolved using then along with our transitions.

window.loadPromise.then(() => timeline.play())

This approach is reusable across the entire codebase, eliminating the issue that would result when an event gets resolved before the animations run. It will defer them until the browser DOMContentLoaded event has passed.

See now that the animation is not kicking off until the red line appears.

The difference is not only on the profiling report — it actually solves an issue we had in a real project.

Wrapping up

In order to act as reminders, I created a list of tips for me that you might find useful as you dig into view transitions in a project:

  • When an animation is happening nothing else should be happening. Run animations after all resources, fetching and business logic have completed.
  • No animation is better than crappy animations If you can’t achieve a good animation, then removing it is a fair sacrifice. The content is more important and showing it is the priority until a good animation solution is in place.
  • Test on slower and older devices. They will make it easier for you to catch spots with weak performance.
  • Profile and base your improvements in metrics. Instead of guessing as you go, like I did, see if you can spot where frames are being dropped or if something looks off and attack that issue first.

That’s it! Best of luck with animating view transitions. Please post a comment if this sparked any questions or if you have used transitions in your app that you’d like to share!

The post Animating Between Views in React appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

Making SVG icon libraries for React apps

Nicolas Gallagher:

At Twitter I used the approach described here to publish the company’s SVG icon library in several different formats: optimized SVGs, plain JavaScript modules, React DOM components, and React Native components.

There is no One True Way© to make an SVG icon system. The only thing that SVG icon systems have in common is that, somehow, some way, SVG is used to show that icon. I gotta find some time to write up a post that goes into all the possibilities there.

One thing different systems tend to share is some kind of build process to turn a folder full of SVG files into a more programmatically digestible format. For example, gulp-svg-sprite takes your folder of SVGs and creates a SVG sprite (chunk of <symbol>s) to use in that type of SVG icon system. Grunticon processes your folder of SVGs into a CSS file, and is capable of enhancing them into inline SVG. Gallagher’s script creates React components out of them, and like he said, that’s great for delivery to different targets as well as performance optimization, like code splitting.

This speaks to the versatility of SVG. It’s just markup, so it’s easy to work with.

Direct Link to ArticlePermalink

The post Making SVG icon libraries for React apps appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]

Rendering Lists Using React Virtualized

Working with data in React is relatively easy because React is designed to handle data as state. The hassle begins when the amount of data you need to consume becomes massive. For example, say you have to handle a dataset which is between 500-1,000 records. This can result in massive loads and lead performance problems. Well, we’re going to look at how we can make use of virtualized lists in React to seamlessly render a long list of data in your application.

We’re going to use the React Virtualized component to get what we need. It will allow us to take large sets of data, process them on the fly, and render them with little-to-no jank.

The setup

React Virtualized already has a detailed set of instructions to get it up and running, so please check out the repo to get started.

We’re going to want data to work with, so we will set up a function which uses faker to create a large data set.

function createRecord(count) {   let records = [];    for (let i = 0; i < count; i++) {     records.push({       username: faker.internet.userName(),       email: faker.internet.email()     });   }   return records; }

Next, we will pass it the number of data records we want to create, like so:

const records = createRecord(1000);

Alright, now we have what we need to work on rendering a list of those records!

Creating a virtualized list

Here’s the list we want to create, sans styling. We could make use of the few presentational styles that the library includes by importing the included CSS file, but we’re going to leave that out in this post.

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

Go ahead and re-run that demo. Crazy fast, right?

You might wonder what the heck React Virtualized is doing behind the scenes to make that happen. Turns out it’s a bunch of crazy and cool sizing, positioning, transforms and transitions that allow the records to scroll in and out of view. The data is already there and rendered. React Virtualized creates a window frame that allows records to slide in and out of view as the user scrolls through it.

To render a virtualized list in React Virtualized, we make use of its List component, which uses a Grid component internally to render the list.

First, we start by setting up rowRenderer, which is responsible for displaying a single row and sets up an index that assigns an ID to each record.

rowRenderer = ({ index, isScrolling, key, style }) => {     return (       <div key={key} style={style}>         <div>{this.props.data[index].username}</div>         <div>{this.props.data[index].email}</div>       </div>     );   };

As you can see, this returns a single div node that contains two additional divs: one for the username and another for the email. You know, a common list pattern to display users.

rowRenderer accepts several parameters. Here’s what they are and what each one does:

  • index: The numeric ID of a record.
  • isScrolling: Indicates if the scrolling is occurring in the List component.
  • isVisible: Determines if a row is visible or out of view.
  • key: The records position in the array.
  • parent: Defines whether the list is a parent or a child of another list.
  • style: A style object to position the row.

Now that we know more about the rowRenderer function, let’s make put it to use in the List component:

<List   rowCount={this.props.data.length}   width={width}   height={height}   rowHeight={rowHeight}   rowRenderer={this.rowRenderer}   overscanRowCount={3} />

You may have noticed a few new parameters. Here’s what they are:

  • rowCount: This takes the numbers of a row in a list that we pass to calculate the length of our list.
  • width: The width of the list.
  • height: The height of the list.
  • rowHeight: This can be a number or a function that returns a row height given its index.
  • rowRenderer: This is responsible for rendering the row. the list is not supposed to be passed directly, so we pass the rowRenderer function that we created in this tutorial.
  • overscanRowCount: This is used to render additional rows in the direction the user scrolls. It reduces the chances of the user scrolling faster than the virtualized content is rendered.

At the end, your code should look something like this;

const { List } = ReactVirtualized  ...  const height = 700; const rowHeight = 40; const width = 800;  class App extends React.Component {   rowRenderer = ({ index, isScrolling, key, style }) => {     return (       <div key={key} style={style}>         <div>{this.props.data[index].username}</div>         <div>{this.props.data[index].email}</div>       </div>     );   };    render() {     return (       <div>         <h2>Details</h2>         <List           rowCount={this.props.data.length}           width={width}           height={height}           rowHeight={rowHeight}           rowRenderer={this.rowRenderer}           overscanRowCount={3}         />       </div>     );   } }

Cell measurer

According to the documentation, a cell measurer is a higher-order component that is used to temporarily render a list. It’s not yet visible to the user at this point, but the data is held and ready to display.

Why should you care about this? The popular use case is a situation where the value of your rowHeight is dynamic. React Virtualized can render the height of the row on render then cache that height so it no longer needs to calculate as data scrolls out of view — it’s always the right height, no matter the content it contains!

First, we create our cache, which can be done in our component’s constructor using CellMeasurerCache:

constructor() {   super()   this.cache = new CellMeasurerCache({     fixedWidth: true,     defaultHeight: 100   }) }

We make use of the cache when we set up the List component;

<List   rowCount={this.props.data.length}   width={rowWidth}   height={listHeight}   deferredMeasurementCache={this.cache}   rowHeight={this.cache.rowHeight}   rowRenderer={this.renderRow}   overscanRowCount={3} />

The value passed to deferredMeasurementCache will be used to temporarily rendering the data, then — as the calculated value for rowHeight comes in — additional rows will flow in like they were always there.

Next, though, we will make use of React Virtualized’s CellMeasurer component inside our rowRenderer function instead of the div we initially set up as a placeholder:

rowRenderer = ({ index, parent, key, style }) => {   return (     <CellMeasurer       key={key}       cache={this.cache}       parent={parent}       columnIndex={0}       rowIndex={index}     >       <div style={style}>         <div>{this.props.data[index].username}</div>         <div>{this.props.data[index].email}</div>       </div>     </CellMeasurer>   );   };

Now the data is fetched, cached and ready to display in the virtual window at will!

Virtualized table

Yeah, so the main point of this post is to cover lists, but what if we actually want to render data to a table instead? React Virtualized has you covered on that front, too. In this case, we will make use of Table and Column components that come baked into React Virtualized.

Here’s how we would put those components to use in our primary App component:

class App extends React.Component {   render() {     return (       <div>         <h2>Details</h2>         <Table           width={500}           height={300}           headerHeight={20}           rowHeight={40}           rowCount={this.props.data.length}           rowGetter={({ index }) => this.props.data[index]}         >           <Column             label='Username'             dataKey='username'             width={100}           />                        <Column             width={200}             label='Email'             dataKey='email'           />         </Table>       </div>     );   } }

The Table component accepts the following parameters:

  • width: The width of the table.
  • height: The height of the table.
  • headerHeight: The table header height.
  • rowHeight: The height of a row given its index.
  • rowCount: This is the initial number of rows we want in the table. It’s the same as the way we defined the number of records we wanted to start with in the List component example.
  • rowGetter: This returns the data of a specific row by its index.

If you take a look at the Column component, you will notice that we put a dataKey parameter to use. That passes the data for each column we called in the dataKey, which receives a unique identifier for that data. Remember that in the function where we create our random data, we make use of two keys; username and email. This is why we have the dataKey of one column set as username and the other set as email.

In conclusion

Hopefully, this walkthrough gives you a good idea of what React Virtualized is capable of doing, how it can make rendering large data sets into lists and tables super fast, and how to put it to use in a project.

We’ve only scratched the surface here. The library is capable of handling a lot of other use cases, like generating placeholders for the data records on scroll, an infinite loading component to fetch and cache data in real-time, a method for allowing arrow keys to navigate through the data, and a slick grid and masonry layouts that we didn’t even cover here.

That should give you a lot to play around with!

Plus, the package is highly maintained. In fact, you can join the Slack group to keep up with the project, contribute to it, and generally get to connect with other folks.

It’s also worth noting that React Virtualized has it own tag in StackOverflow and that can be a good resource to find questions other people have asked about it, or even post your own questions.

Oh, and if you’ve put React Virtualized to use on a project, we’d love to know it! Share it with us in the comments with some notes on how you approached it or what you learned from it.

The post Rendering Lists Using React Virtualized appeared first on CSS-Tricks.

CSS-Tricks

, , , ,
[Top]