Understanding React `setState`

React components can, and often do, have state. State can be anything, but think of things like whether a user is logged in or not and displaying the correct username based on which account is active. Or an array of blog posts. Or if a modal is open or not and which tab within it is active.

React components with state render reconciliation. The reconciliation process is the way React updates the DOM, by making changes to the component based on the change in state. When the request to setState() is triggered, React creates a new tree containing the reactive elements in the component (along with the updated state). This tree is used to figure out how the Search component’s setState Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Let’s register the component and define the markup for the Understanding React `setState` appeared first on CSS-Tricks.

What are Higher-Order Components in React?

If you have been in the React ecosystem for a while, there is a possibility that you have heard about Higher Order Components. Let’s look at a simple implementation while also trying to explain the core idea. From here you should get a good idea of how they work and even put them to use.

Why Higher-Order Components?

As you build React applications, you will run into situations where you want to share the same functionality across multiple components.

For example: you need to manage the state of currently logged in users in your application. Instead of managing that state across all of the components that need that state, you could create a higher-order component to separate the logged in user state into a container component, then pass that state to the components that will make use of it.

The components that receive state from the higher-order component will function as presentational components. State gets passed to them and they conditionally render UI based on it. They do not bother with the management of state.

Let’s see another example. Say you have three JSON files in your application. These files contain different data that will be loaded in your application in three different components. You want to give your users the ability to search the data loaded from these files. You could implement a search feature in all three of the components. This duplication may not be an issue at first, but as your application grows and more components need this functionality, the constant duplication will be cumbersome and prone to problems.

A better way forward is to create a higher-order component to handle the search functionality. With it, you can wrap the other components individually in your higher-order component.

How do Higher-Order Components Work?

The hoc Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Conclusion

Higher-Order Components do not have to be scary. After understanding the basics, you can put the concept to use by abstracting away functionalities that can be shared among different components.

More Resources

  • Higher-Order Components – React Documentation
  • React Higher Order Components in depth – by Fran Guijarro
  • Higher Order Components in React – by Chris Nwamba
  • More Examples – CodePen collection

The post What are Higher-Order Components in React? appeared first on CSS-Tricks.

React’s New Context API Explained

In this video tutorial, Wes Bos looks into the new context API and the problem that it’s trying to solve:

React 16.3 has a new Context API which makes accessing data and functions anywhere in your application a snap. If you ever find yourself passing data down via props 4-5 levels deep, context might be what you are looking for.

Don’t forget about Neal Fennimore’s recent post on putting things into context. It covers the concept in great detail and provides some workarounds for the quirks you might encounter when using it.

Direct Link to Article — Permalink

The post React’s New Context API Explained appeared first on CSS-Tricks.

Putting Things in Context With React

Context is currently an experimental API for React – but soon to be a first class citizen! There are a lot of reasons it is interesting but perhaps the most is that it allows for parent components to pass data implicitly to their children, no matter how deep the component tree is. In other words, data can be added to a parent component and then any child can tap into it.

See the Pen React Context Lights by Neal Fennimore (@nealfennimore) on CodePen.

While this is often the use case for using something like Redux, it’s nice to use if you do not need complex data management. Think about that! We create a custom downstream of data, deciding which props are passed and at which levels. Pretty cool.

Context is great in areas of where you have a lot of components that depend on a single piece of data, but are deep within the component tree. Explicitly passing each prop to each individual component can often be overwhelming and it is a lot easier just to use context here.

For example, let’s consider how we would normally pass props down the tree. In this case, we’re passing the color red using props on each component in order to move it on down the stream.

class Parent extends React.Component { render(){ return <Child color="red" />; }
} class Child extends React.Component { render(){ return <GrandChild color={this.props.color} /> }
} class GrandChild extends React.Component { render(){ return ( <div style={{color: this.props.color}}> Yep, I'm the GrandChild </div> ); }
}

What if we never wanted the Child component to have the prop in the first place? Context saves us having to go through the Child component with color and pass it directly from the Parent to the GrandChild:

class Parent extends React.Component { // Allow children to use context getChildContext() { return { color: 'red' }; } render(){ return <Child />; }
} Parent.childContextTypes = { color: PropTypes.string
}; class Child extends React.Component { render() { // Props is removed and context flows through to GrandChild return <GrandChild /> }
} class GrandChild extends React.Component { render() { return ( <div style={{color: this.context.color}}> Yep, I'm still the GrandChild </div> ); }
} // Expose color to the GrandChild
GrandChild.contextTypes = { color: PropTypes.string
};

While slightly more verbose, the upside is exposing the color anywhere down in the component tree. Well, sometimes…

There’s Some Gotchas

You can’t always have your cake and eat it too, and context in it’s current form is no exception. There are a few underlying issues that you’ll more than likely come into contact with if you end up using context for all but the simplest cases.

Context is great for being used on an initial render. Updating context on the fly? Not so much. A common issue with context is that context changes are not always reflected in a component.

Let’s dissect these gotchas in more detail.

Gotcha 1: Using Pure Components

Context is hard when using PureComponent since by default it does not perform any shallow diffing by default with context. Shallow diffing with PureComponent is testing for whether the values of the object are strictly equal. If they’re not, then (and only then) will the component update. But since context is not checked, well… nothing happens.

See the Pen React Context Lights with PureComponents by Neal Fennimore (@nealfennimore) on CodePen.

Gotcha 2: Should Component Update? Maybe.

Context also does not update if a component’s shouldComponentUpdate returns false. If you have a custom shouldComponentUpdate method, then you’ll also need to take context into consideration. To enable updates with context, we could update each individual component with a custom shouldComponentUpdate that looks something like this.

import shallowEqual from 'fbjs/lib/shallowEqual'; class ComponentThatNeedsColorContext extends React.PureComponent { // nextContext will show color as soon as we apply ComponentThatNeedsColorContext.contextTypes // NOTE: Doing the below will show a console error come react v16.1.1 shouldComponentUpdate(nextProps, nextState, nextContext){ return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState) || !shallowEqual(this.context, nextContext); }
} ComponentThatNeedsColorContext.contextTypes = { color: PropTypes.string
};

However, this does not solve the issue of an intermediary PureComponent between the parent and the child blocking context updates. This means that every PureComponent between the parent and child would need to have contextTypes defined on it, and they would also need to have an updated shouldComponentUpdate method. And at this point, that’s a lot of work for very little gain.

Better Approaches to the Gotchas

Fortunately, we have some ways to work around the gotchas.

Approach 1: Use a Higher Order Component

A Higher Order Component can read from context and pass the needed values on to the next component as a prop.

import React from 'react'; const withColor = (WrappedComponent) => { class ColorHOC extends React.Component { render() { const { color } = this.context; return <WrappedComponent style={{color: color}} {...this.props} /> } } ColorHOC.contextTypes = { color: React.PropTypes.string }; return ColorHOC;
}; export const Button = (props)=> <button {...props}>Button</button> // ColoredButton will render with whatever color is currently in context with a style prop
export const ColoredButton = withColor( Button );

See the Pen React Context Lights with HOC by Neal Fennimore (@nealfennimore) on CodePen.

Approach 2: Use Render Props

Render Props allow us to use props to share code between two components.

class App extends React.Component { getChildContext() { return { color: 'red' } } render() { return <Button /> }
} App.childContextTypes = { color: React.PropTypes.string
} // Hook 'Color' into 'App' context
class Color extends React.Component { render() { return this.props.render(this.context.color); }
} Color.contextTypes = { color: React.PropTypes.string
} class Button extends React.Component { render() { return ( <button type="button"> {/* Return colored text within Button */} <Color render={ color => ( <Text color={color} text="Button Text" /> ) } /> </button> ) }
} class Text extends React.Component { render(){ return (  {this.props.text}  ) }
} Text.propTypes = { text: React.PropTypes.string, color: React.PropTypes.string,
}</Color></button>

Approach 3: Dependency Injection

A third way we can work around these gotchas is to use Dependency Injection to limit the context API and allow components to subscribe as needed.

The New Context

The new way of using context, which is currently slated for the next minor release of React (16.3), has the benefits of being more readable and easier to write without the “gotchas” from previous versions. We now have a new method called createContext, which defines a new context and returns both a Provider and Consumer.

The Provider establishes a context that all sub-components can hook into. It’s hooked in via Consumer which uses a render prop. The first argument of that render prop function, is the value which we have given to the Provider. By updating the value within the Provider, all consumers will update to reflect the new value.

As a side benefit with using the new context, we no longer have to use childContextTypes, getChildContext, and contextTypes.

const ColorContext = React.createContext('color');
class ColorProvider extends React.Component { render(){ return ( <ColorContext.Provider value={'red'}> { this.props.children } </ColorContext.Provider> ) }
} class Parent extends React.Component { render(){ // Wrap 'Child' with our color provider return ( <ColorProvider> <Child /> </ColorProvider> ); }
} class Child extends React.Component { render(){ return <GrandChild /> }
} class GrandChild extends React.Component { render(){ // Consume our context and pass the color into the style attribute return ( <ColorContext.Consumer> {/* 'color' is the value from our Provider */} { color => ( <div style={{color: color}}> Yep, I'm still the GrandChild </div> ) } </ColorContext.Consumer> ); }
}

Separate Contexts

Since we have more granular control in how we expose context and to what components are allowed to use it, we can individually wrap components with different contexts, even if they live within the same component. We can see this in the next example, whereby using the LightProvider twice, we can give two components a separate context.

See the Pen React Context Lights with new Context by Neal Fennimore (@nealfennimore) on CodePen.

Conclusion

Context is a powerful API, but it’s also very easy to use incorrectly. There are also a few caveats to using it, and it can be very hard to figure out issues when components go awry. While Higher-Order Components and dependency injection offer alternatives for most cases, context can be used beneficially in isolated portions of your code base.

With the next context though, we no longer have to worry about the gotchas we had with the previous version. It removes having to define contextTypes on individual components and opens up the potential for defining new contexts in a reusable manner.

The post Putting Things in Context With React appeared first on CSS-Tricks.

React State From the Ground Up

As you begin to learn React, you will be faced with understanding what state is. State is hugely important in React, and perhaps a big reason you’ve looked into using React in the first place. Let’s take a stab at understanding what state is and how it works.

What is State?

State, in React, is a plain JavaScript object that allows you keep track of a component’s data. The state of a component can change. A change to the state of a component depends on the functionality of the application. Changes can be based on user response, new messages from server-side, network response, or anything.

Component state is expected to be private to the component and controlled by the same component. To make changes to a component’s state, you have to make them inside the component — the initialization and updating of the component’s state.

Class Components

States is only available to components that are called class components. The main reason why you will want to use class components over their counterpart, functional components, is that class components can have state. Let’s see the difference. Functional components are JavaScript functions, like this:

const App = (props) => { return ( <div> { this.props } </div> )
}

If the functionality you need from your component is as simple as the one above, then a functional component is the perfect fit. A class component will look a lot more complex than that.

class App extends React.Component { constructor(props) { super(props) this.state = { username: 'johndoe' } } render() { const { username } = this.state return( <div> { username } </div> ) }
}

Above, I am setting the state of the component’s username to a string.

The Constructor

According to the official documentation, the constructor is the right place to initialize state. Initializing state is done by setting this.state to an object, like you can see above. Remember: state is a plain JavaScript object. The initial state of the App component has been set to a state object which contains the key username, and its value johndoe using this.state = { username: 'johndoe' }.

Initializing a component state can get as complex as what you can see here:

constructor(props) { super(props) this.state = { currentTime: 0, status: false, btnOne: false, todoList: [], name: 'John Doe' }
}

Accessing State

An initialized state can be accessed in the render() method, as I did above.

render() { const { username } = this.state return( <div> { username } </div> )
}

An alternative to the above snippet is:

render() { return( <div> { this.state.username } </div> )
}

The difference is that I extracted the username from state in the first example, but it can also be written as const status = this.state.username. Thanks to ES6 destructuring, I do not have to go that route. Do not get confused when you see things like this. It is important to know that I am not reassigning state when I did that. The initial setup of state was done in the constructor, and should not be done again – never update your component state directly.

A state can be accessed using this.state.property-name. Do not forget that aside from the point where you initialized your state, the next time you are to make use of this.state is when you want to access the state.

Updating State

The only permissible way to update a component’s state is by using setState(). Let’s see how this works practically.

First, I will start with creating the method that gets called to update the component’s username. This method should receive an argument, and it is expected to use that argument to update the state.

handleInputChange(username) { this.setState({username})
}

Once again, you can see that I am passing in an object to setState(). With that done, I will need to pass this function to the event handler that gets called when the value of an input box is changed. The event handler will give the context of the event that was triggered which makes it possible to obtain the value entered in the input box using event.target.value. This is the argument passed to handleInputChange() method. So, the render method should look like this.

render() { const { username } = this.state return ( <div> <div> <input type="text" value={this.state.username} onChange={event => this.handleInputChange(event.target.value)} /> </div> <p>Your username is, {username}</p> </div> )
}

Each time setState() is called, a request is sent to React to update the DOM using the newly updated state. Having this mindset makes you understand that state update can be delayed.

Your component should look like this;

class App extends React.Component { constructor(props) { super(props) this.state = { username: 'johndoe' } } handleInputChange(username) { this.setState({username}) } render() { const { username } = this.state return ( <div> <div> <input type="text" value={this.state.username} onChange={event => this.handleInputChange(event.target.value)} /> </div> <p>Your username is, {username}</p> </div> ) }
}

Passing State as Props

A state can be passed as props from a parent to the child component. To see this in action, let’s create a new component for creating a To Do List. This component will have an input field to enter daily tasks and the tasks will be passed as props to the child component.

Try to create the parent component on your own, using the lessons you have learned thus far.

Let’s start with creating the initial state of the component.

class App extends React.Component { constructor(props) { super(props) this.state = { todoList: [] } } render() { return() }
}

The component’s state has its todoList set to an empty array. In the render() method, I want to return a form for submitting tasks.

render() { const { todoList } = this.state return ( <div> <h2>Enter your to-do</h2> <form onSubmit={this.handleSubmit}> <label>Todo Item</label> <input type="text" name="todoitem" /> <button type="submit">Submit</button> </form> </div > )
}

Each time a new item is entered and the submit button is clicked, the method handleSubmit gets called. This method will be used to update the state of the component. The way I want to update it is by using concat to add the new value in the todoList array. Doing so will set the value for todoList inside the setState() method. Here’s how that should look:

handleSubmit = (event) => { event.preventDefault() const value = (event.target.elements.todoitem.value) this.setState(({todoList}) => ({ todoList: todoList.concat(value) }))
}

The event context is obtained each time the submit button is clicked. We use event.preventDefault() to stop the default action of submission which would reload the page. The value entered in the input field is assigned a variable called value, which is then passed an argument when todoList.concat() is called. React updates the state of todoList by adding the new value to the initial empty array. This new array becomes the current state of todoList. When another item is added, the cycle repeats.

A chart illustrating the cycle explained above.

The goal here is to pass the individual item to a child component as props. For this tutorial, we’ll call it the TodoItem component. Add the code snippet below inside the parent div which you have in render() method.

<div> <h2>Your todo lists include:</h2> { todoList.map(i => <TodoItem item={i} /> )}
</div>

You’re using map to loop through the todoList array, which means the individual item is then passed to the TodoItem component as props. To make use of this, you need to have a TodoItem component that receives props and renders it on the DOM. I will show you how to do this using functional and class components.

Written as a functional component:

const TodoItem = (props) => { return ( <div> {props.item} </div> )
}

For the class component, it would be:

class TodoItem extends React.Component { constructor(props) { super(props) } render() { const {item} = this.props return ( <div> {item} </div> ) }
}

If there is no need to manage state in this component, you are better off using functional component.

Leveling Up

You will be handling state very often while developing React application. With all the areas covered above, you should have the confidence of being able to dive into the advanced part of state management in React. To dig deeper, I recommend React’s official documentation on State and Lifecycle as well as Uber’s React Guide on Props vs State.

The post React State From the Ground Up appeared first on CSS-Tricks.

WordPress + React

I posted just 2 months ago about Foxhound and how I found it pretty cool, but also curious that it was one of very few themes around that combine the WordPress JSON API and React, even though they seem like a perfect natural fit. Like a headless CMS, almost.

Since then, a few more things have crossed my desk of people doing more with this idea and combination.

Maxime Laboissonniere wrote Strapping React.js on a WordPress Backend: WP REST API Example:

I’ll use WordPress as a backend, and WordPress REST API to feed data into a simple React e-commerce SPA:

  • Creating products with the WP Advanced Custom Fields plugin
  • Mapping custom fields to JSON payload
  • Consuming the JSON REST API with React
  • Rendering products in our store

Perhaps more directly usable, Postlight have put out a Starter Kit. Gina Trapani:

People who publish on the web love WordPress. Engineers love React. With some research, configuration, and trial and error, you can have both — but we’d like to save you the work.

Here’s that repo.

Direct Link to Article — Permalink


WordPress + React is a post from CSS-Tricks

Accessible Web Apps with React, TypeScript, and AllyJS

Accessibility is an aspect of web development that is often overlooked. I would argue that it is as vital as overall performance and code reusability. We justify our endless pursuit of better performance and responsive design by citing the users, but ultimately these pursuits are done with the user’s device in mind, not the user themselves and their potential disabilities or restrictions.

A responsive app should be one that delivers its content based on the needs of the user, not only their device.

Luckily, there are tools to help alleviate the learning curve of accessibility-minded development. For example, GitHub recently released their accessibility error scanner, AccessibilityJS and Deque has aXe. This article will focus on a different one: Ally.js, a library simplifying certain accessibility features, functions, and behaviors.


One of the most common pain points regarding accessibility is dialog windows.

There’re a lot of considerations to take in terms of communicating to the user about the dialog itself, ensuring ease of access to its content, and returning to the dialog’s trigger upon close.

A demo on the Ally.js website addresses this challenge which helped me port its logic to my current project which uses React and TypeScript. This post will walk through building an accessible dialog component.

Demo of accessible dialog window using Ally.js within React and TypeScript

View the live demo

Project Setup with create-react-app

Before getting into the use of Ally.js, let’s take a look at the initial setup of the project. The project can be cloned from GitHub or you can follow along manually. The project was initiated using create-react-app in the terminal with the following options:

create-react-app my-app --scripts-version=react-scripts-ts

This created a project using React and ReactDOM version 15.6.1 along with their corresponding @types.

With the project created, let’s go ahead and take a look at the package file and project scaffolding I am using for this demo.

Project architecture and package.json file

As you can see in the image above, there are several additional packages installed but for this post we will ignore those related to testing and focus on the primary two, ally.js and babel-polyfill.

Let’s install both of these packages via our terminal.

yarn add ally.js --dev && yarn add babel-polyfill --dev

For now, let’s leave `/src/index.tsx` alone and hop straight into our App container.

App Container

The App container will handle our state that we use to toggle the dialog window. Now, this could also be handled by Redux but that will be excluded in lieu of brevity.

Let’s first define the state and toggle method.

interface AppState { showDialog: boolean;
} class App extends React.Component<{}, AppState> { state: AppState; constructor(props: {}) { super(props); this.state = { showDialog: false }; } toggleDialog() { this.setState({ showDialog: !this.state.showDialog }); }
}

The above gets us started with our state and the method we will use to toggle the dialog. Next would be to create an outline for our render method.

class App extends React.Component<{}, AppState> { ... render() { return ( <div className="site-container"> <header> <h1>Ally.js with React &amp; Typescript</h1> </header> <main className="content-container"> <div className="field-container"> <label htmlFor="name-field">Name:</label> <input type="text" id="name-field" placeholder="Enter your name" /> </div> <div className="field-container"> <label htmlFor="food-field">Favourite Food:</label> <input type="text" id="food-field" placeholder="Enter your favourite food" /> </div> <div className="field-container"> <button className='btn primary' tabIndex={0} title='Open Dialog' onClick={() => this.toggleDialog()} > Open Dialog </button> </div> </main> </div> ); }
}

Don’t worry much about the styles and class names at this point. These elements can be styled as you see fit. However, feel free to clone the GitHub repo for the full styles.

At this point we should have a basic form on our page with a button that when clicked toggles our showDialog state value. This can be confirmed by using React’s Developer Tools.

So let’s now have the dialog window toggle as well with the button. For this let’s create a new Dialog component.

Dialog Component

Let’s look at the structure of our Dialog component which will act as a wrapper of whatever content (children) we pass into it.

interface Props { children: object; title: string; description: string; close(): void;
} class Dialog extends React.Component<Props> { dialog: HTMLElement | null; render() { return ( <div role="dialog" tabIndex={0} className="popup-outer-container" aria-hidden={false} aria-labelledby="dialog-title" aria-describedby="dialog-description" ref={(popup) => { this.dialog = popup; } } > <h5 id="dialog-title" className="is-visually-hidden" > {this.props.title} </h5> <p id="dialog-description" className="is-visually-hidden" > {this.props.description} </p> <div className="popup-inner-container"> <button className="close-icon" title="Close Dialog" onClick={() => { this.props.close(); }} > × </button> {this.props.children} </div> </div> ); }
}

We begin this component by creating the Props interface. This will allow us to pass in the dialog’s title and description, two important pieces for accessibility. We will also pass in a close method, which will refer back to the toggleDialog method from the App container. Lastly, we create the functional ref to the newly created dialog window to be used later.

The following styles can be applied to create the dialog window appearance.

.popup-outer-container { align-items: center; background: rgba(0, 0, 0, 0.2); display: flex; height: 100vh; justify-content: center; padding: 10px; position: absolute; width: 100%; z-index: 10;
} .popup-inner-container { background: #fff; border-radius: 4px; box-shadow: 0px 0px 10px 3px rgba(119, 119, 119, 0.35); max-width: 750px; padding: 10px; position: relative; width: 100%;
} .popup-inner-container:focus-within { outline: -webkit-focus-ring-color auto 2px;
} .close-icon { background: transparent; color: #6e6e6e; cursor: pointer; font: 2rem/1 sans-serif; position: absolute; right: 20px; top: 1rem;
}

Now, let’s tie this together with the App container and then get into Ally.js to make this dialog window more accessible.

App Container

Back in the App container, let’s add a check inside of the render method so any time the showDialog state updates, the Dialog component is toggled.

class App extends React.Component<{}, AppState> { ... checkForDialog() { if (this.state.showDialog) { return this.getDialog(); } else { return false; } } getDialog() { return ( <Dialog title="Favourite Holiday Dialog" description="Add your favourite holiday to the list" close={() => { this.toggleDialog(); }} > <form className="dialog-content"> <header> <h1 id="dialog-title">Holiday Entry</h1> <p id="dialog-description">Please enter your favourite holiday.</p> </header> <section> <div className="field-container"> <label htmlFor="within-dialog">Favourite Holiday</label> <input id="within-dialog" /> </div> </section> <footer> <div className="btns-container"> <Button type="primary" clickHandler={() => { this.toggleDialog(); }} msg="Save" /> </div> </footer> </form> </Dialog> ); } render() { return ( <div className="site-container"> {this.checkForDialog()} ... ); }
}

What we’ve done here is add the methods checkForDialog and getDialog.

Inside of the render method, which runs any time the state updates, there is a call to run checkForDialog. So upon clicking the button, the showDialog state will update, causing a re-render, calling checkForDialog again. Only now, showDialog is true, triggering getDialog. This method returns the Dialog component we just built to be rendered onto the screen.

The above sample includes a Button component that has not been shown.

Now, we should have the ability to open and close our dialog. So let’s take a look at what problems exist in terms of accessibility and how we can address them using Ally.js.


Using only your keyboard, open the dialog window and try to enter text into the form. You’ll notice that you must tab through the entire document to reach the elements within the dialog. This is a less-than-ideal experience. When the dialog opens, our focus should be the dialog  –  not the content behind it. So let’s look at our first use of Ally.js to begin remedying this issue.

Ally.js

Ally.js is a library providing various modules to help simplify common accessibility challenges. We will be using four of these modules for the Dialog component.

The .popup-outer-container acts as a mask that lays over the page blocking interaction from the mouse. However, elements behind this mask are still accessible via keyboard, which should be disallowed. To do this the first Ally module we’ll incorporate is maintain/disabled. This is used to disable any set of elements from being focussed via keyboard, essentially making them inert.

Unfortunately, implementing Ally.js into a project with TypeScript isn’t as straightforward as other libraries. This is due to Ally.js not providing a dedicated set of TypeScript definitions. But no worries, as we can declare our own modules via TypeScript’s types files.

In the original screenshot showing the scaffolding of the project, we see a directory called types. Let’s create that and inside create a file called `global.d.ts`.

Inside of this file let’s declare our first Ally.js module from the esm/ directory which provides ES6 modules but with the contents of each compiled to ES5. These are recommended when using build tools.

declare module 'ally.js/esm/maintain/disabled';

With this module now declared in our global types file, let’s head back into the Dialog component to begin implementing the functionality.

Dialog Component

We will be adding all the accessibility functionality for the Dialog to its component to keep it self-contained. Let’s first import our newly declared module at the top of the file.

import Disabled from 'ally.js/esm/maintain/disabled';

The goal of using this module will be once the Dialog component mounts, everything on the page will be disabled while filtering out the dialog itself.

So let’s use the componentDidMount lifecycle hook for attaching any Ally.js functionality.

interface Handle { disengage(): void;
} class Dialog extends React.Component<Props, {}> { dialog: HTMLElement | null; disabledHandle: Handle; componentDidMount() { this.disabledHandle = Disabled({ filter: this.dialog, }); } componentWillUnmount() { this.disabledHandle.disengage(); } ...
}

When the component mounts, we store the Disabled functionality to the newly created component property disableHandle. Because there are no defined types yet for Ally.js we can create a generic Handle interface containing the disengage function property. We will be using this Handle again for other Ally modules, hence keeping it generic.

By using the filter property of the Disabled import, we’re able to tell Ally.js to disable everything in the document except for our dialog reference.

Lastly, whenever the component unmounts we want to remove this behaviour. So inside of the componentWillUnmount hook, we disengage() the disableHandle.


We will now follow this same process for the final steps of improving the Dialog component. We will use the additional Ally modules:

  • maintain/tab-focus
  • query/first-tabbable
  • when/key

Let’s update the `global.d.ts` file so it declares these additional modules.

declare module 'ally.js/esm/maintain/disabled';
declare module 'ally.js/esm/maintain/tab-focus';
declare module 'ally.js/esm/query/first-tabbable';
declare module 'ally.js/esm/when/key';

As well as import them all into the Dialog component.

import Disabled from 'ally.js/esm/maintain/disabled';
import TabFocus from 'ally.js/esm/maintain/tab-focus';
import FirstTab from 'ally.js/esm/query/first-tabbable';
import Key from 'ally.js/esm/when/key';

Tab Focus

After disabling the document with the exception of our dialog, we now need to restrict tabbing access further. Currently, upon tabbing to the last element in the dialog, pressing tab again will begin moving focus to the browser’s UI (such as the address bar). Instead, we want to leverage tab-focus to ensure the tab key will reset to the beginning of the dialog, not jump to the window.

class Dialog extends React.Component<Props> { dialog: HTMLElement | null; disabledHandle: Handle; focusHandle: Handle; componentDidMount() { this.disabledHandle = Disabled({ filter: this.dialog, }); this.focusHandle = TabFocus({ context: this.dialog, }); } componentWillUnmount() { this.disabledHandle.disengage(); this.focusHandle.disengage(); } ...
}

We follow the same process here as we did for the disabled module. Let’s create a focusHandle property which will assume the value of the TabFocus module import. We define the context to be the active dialog reference on mount and then disengage() this behaviour, again, when the component unmounts.

At this point, with a dialog window open, hitting tab should cycle through the elements within the dialog itself.

Now, wouldn’t it be nice if the first element of our dialog was already focused upon opening?

First Tab Focus

Leveraging the first-tabbable module, we are able to set focus to the first element of the dialog window whenever it mounts.

class Dialog extends React.Component<Props> { dialog: HTMLElement | null; disabledHandle: Handle; focusHandle: Handle; componentDidMount() { this.disabledHandle = Disabled({ filter: this.dialog, }); this.focusHandle = TabFocus({ context: this.dialog, }); let element = FirstTab({ context: this.dialog, defaultToContext: true, }); element.focus(); } ...
}

Within the componentDidMount hook, we create the element variable and assign it to our FirstTab import. This will return the first tabbable element within the context that we provide. Once that element is returned, calling element.focus() will apply focus automatically.

Now, that we have the behavior within the dialog working pretty well, we want to improve keyboard accessibility. As a strict laptop user myself (no external mouse, monitor, or any peripherals) I tend to instinctively press esc whenever I want to close any dialog or popup. Normally, I would write my own event listener to handle this behavior but Ally.js provides the when/key module to simplify this process as well.

class Dialog extends React.Component<Props> { dialog: HTMLElement | null; disabledHandle: Handle; focusHandle: Handle; keyHandle: Handle; componentDidMount() { this.disabledHandle = Disabled({ filter: this.dialog, }); this.focusHandle = TabFocus({ context: this.dialog, }); let element = FirstTab({ context: this.dialog, defaultToContext: true, }); element.focus(); this.keyHandle = Key({ escape: () => { this.props.close(); }, }); } componentWillUnmount() { this.disabledHandle.disengage(); this.focusHandle.disengage(); this.keyHandle.disengage(); } ...
}

Again, we provide a Handle property to our class which will allow us to easily bind the esc functionality on mount and then disengage() it on unmount. And like that, we’re now able to easily close our dialog via the keyboard without necessarily having to tab to a specific close button.

Lastly (whew!), upon closing the dialog window, the user’s focus should return to the element that triggered it. In this case, the Show Dialog button in the App container. This isn’t built into Ally.js but a recommended best practice that, as you’ll see, can be added in with little hassle.

class Dialog extends React.Component<Props> { dialog: HTMLElement | null; disabledHandle: Handle; focusHandle: Handle; keyHandle: Handle; focusedElementBeforeDialogOpened: HTMLInputElement | HTMLButtonElement; componentDidMount() { if (document.activeElement instanceof HTMLInputElement || document.activeElement instanceof HTMLButtonElement) { this.focusedElementBeforeDialogOpened = document.activeElement; } this.disabledHandle = Disabled({ filter: this.dialog, }); this.focusHandle = TabFocus({ context: this.dialog, }); let element = FirstTab({ context: this.dialog, defaultToContext: true, }); this.keyHandle = Key({ escape: () => { this.props.close(); }, }); element.focus(); } componentWillUnmount() { this.disabledHandle.disengage(); this.focusHandle.disengage(); this.keyHandle.disengage(); this.focusedElementBeforeDialogOpened.focus(); } ...
}

What has been done here is a property, focusedElementBeforeDialogOpened, has been added to our class. Whenever the component mounts, we store the current activeElement within the document to this property.

It’s important to do this before we disable the entire document or else document.activeElement will return null.

Then, like we had done with setting focus to the first element in the dialog, we will use the .focus() method of our stored element on componentWillUnmount to apply focus to the original button upon closing the dialog. This functionality has been wrapped in a type guard to ensure the element supports the focus() method.


Now, that our Dialog component is working, accessible, and self-contained we are ready to build our App. Except, running yarn test or yarn build will result in an error. Something to this effect:

[path]/node_modules/ally.js/esm/maintain/disabled.js:21 import nodeArray from '../util/node-array'; ^^^^^^ SyntaxError: Unexpected token import

Despite Create React App and its test runner, Jest, supporting ES6 modules, an issue is still caused with the ESM declared modules. So this brings us to our final step of integrating Ally.js with React, and that is the babel-polyfill package.

All the way in the beginning of this post (literally, ages ago!), I showed additional packages to install, the second of which being babel-polyfill. With this installed, let’s head to our app’s entry point, in this case ./src/index.tsx.

Index.tsx

At the very top of this file, let’s import babel-polyfill. This will emulate a full ES2015+ environment and is intended to be used in an application rather than a library/tool.

import 'babel-polyfill';

With that, we can return to our terminal to run the test and build scripts from create-react-app without any error.

Demo of accessible dialog window using Ally.js within React and TypeScript

View the live demo


Now that Ally.js is incorporated into your React and TypeScript project, more steps can be taken to ensure your content can be consumed by all users, not just all of their devices.

For more information on accessibility and other great resources please visit these resources:

  • Accessible Web Apps with React, TypeScript & Ally.js on Github
  • Start Building Accessible Web Applications Today
  • HTML Codesniffer
  • Web Accessibility Best Practices
  • Writing CSS with Accessibility in Mind
  • Accessibility Checklist

Accessible Web Apps with React, TypeScript, and AllyJS is a post from CSS-Tricks