Working With Events in React

Most of the behavior in an application revolves around events. User enters a value in the registration form? Event. User hits the submit button? Another event. Events are triggered a number of ways and we build applications to listen for them in order to do something else in response.

You may already be super comfortable working with events based on your existing JavaScript experience. However, React has a distinct way of handling them. Rather than directly targeting DOM events, React wraps them in their own event wrapper. But we’ll get into that.

Let’s go over how to create, add and listen for events in React.

Creating Events

We’ll start by creating a form that has an input and a button. An event will be triggered when a value is entered. The button is used to call a function which will reverse that value.

Here’s how it’ll work:

  • An empty input field allows the user to enter text.
  • An onChange event is triggered when values are entered in the input. This calls a function — handleChange() — that is used to set a new state for the input.
  • When the “Reverse Text” button is clicked, another event is triggered. This calls a function — handleReverse() — to set a new state for reversedText.

Here’s that translated into code:

class App extends React.Component { state = { /* Initial State */ input: "", reversedText: "" }; /* handleChange() function to set a new state for input */ handleChange = event => { const value = event.target.value; this.setState({ input: value }); }; /* handleReverse() function to reverse the input and set that as new state for reversedText */ handleReverse = event => { event.preventDefault(); const text = this.state.input; this.setState({ reversedText: text .split("") .reverse() .join("") }); }; render() { return ( <React.Fragment> { /* handleReverse() is called when the form is submitted */ } <form onSubmit={this.handleReverse}> <div> { /* Render input entered */} <label>Text: {this.state.input}</label> </div> <div> { /* handleChange() is triggered when text is entered */ } <input type="text" value={this.state.input} onChange={this.handleChange} placeholder="Enter a text" /> </div> <div> <button>Reverse Text</button> </div> </form> { /* Render reversed text */} <p>Reversed Text: {this.state.reversedText}</p> </React.Fragment> ); }
}}

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

Listening to component events

Let’s say you have a component like this;

class IncrementButton extends React.Component{ render() { return ( <React.Fragment> <button>+</button> </React.Fragment> ) }
}

Will including it in your App component like this work?

class App extends React.Component{ state = { count: 0 } handleIncrement = (event) => { this.setState({ count: this.state.count + 1}) } render() { return( <React.Fragment> <h1>{this.state.count}</h1> <IncrementButton onClick={this.handleIncrement} /> </React.Fragment> ) }
}

No, it won’t because you can only listen to events on DOM elements. We touched on this at the beginning of the post, but React components are wrappers for DOM elements. That means we essentially have a layer that we need to pass through to listen for the event.

The way around this is to pass the event handler as a prop to the child component. Then the prop is passed down to the click event as an attribute like so:

class IncrementButton extends React.Component{ render() { return ( <React.Fragment> <button onClick={this.props.increaseButton}>+</button> </React.Fragment> ) }
} class App extends React.Component{ state = { count: 0 } handleIncrement = (event) => { this.setState({ count: this.state.count + 1}) } render() { return( <React.Fragment> <h1>{this.state.count}</h1> <IncrementButton increaseButton={this.handleIncrement} /> </React.Fragment> ) }
}

See the Pen React Event Pen – Component Events by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

You could make use of a stateless functional component instead:

const IncrementButton = (props) => { return ( <React.Fragment> <button onClick={props.increaseButton}>+</button> </React.Fragment> )
}

Adding event listeners

There may be times when you want to make use of certain DOM events that are triggered when the component is mounted. Let’s see this using the resize event — we want to see the width of the window whenever it is resized.

class App extends React.Component{ state = { windowWith: window.innerWidth } handleResize = (event) => { this.setState({ windowWith: window.innerWidth }) } render() { return( <React.Fragment> <h1>Window Width</h1> <h1>{this.state.windowWith}</h1> </React.Fragment> ) }
}

If we create a component and try it out like we have below, then the event will not be triggered. We’ll need to add the event listener (handleResize() in this case) and the event type like we have here:

class App extends React.Component{ state = { windowWith: window.innerWidth } handleResize = (event) => { this.setState({ windowWith: window.innerWidth }) } componentDidMount() { window.addEventListener('resize', this.handleResize) } componentDidUnmount() { window.removeEventListener('resize', this.handleResize) } render() { return( <React.Fragment> <h1>Window Width</h1> <h1>{this.state.windowWith}</h1> </React.Fragment> ) }
}

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

Now, the event listener will be added when the component mounts. That means our component is actively listening to the browser window and will display its width when it updates.

In summary

OK, so we covered quite a bit of ground in a very small amount of space. We learned that React does not connect directly to a DOM event, but rather Synthetic Events that are wrappers for DOM events. We dug into the process for creating event listeners so that they attach to Synthetic Events and, from there, made sure that a component will update when those events are triggered.

Additional resources

  • Handling Events – React documentation
  • SyntheticEvent – React documentation, including all event types
  • 5 Essential React Concepts to to Know Before Learning Redux – Chris Nwamba provides a nice overview for event handling in React in the second part of this series

The post Working With Events in React appeared first on CSS-Tricks.

Render Caching for React

Server Side Rendering (SSR) is a very useful technique that makes web apps appear faster. The initial HTML is displayed before the JavaScript is parsed and, while the user is deciding what to tap on, our handlers are ready.

Server side rendering in React requires additional work to setup and has server costs. Furthermore, if your server team cannot have JavaScript running on your servers, you are stuck. It significantly complicates the hydrate conditionally. Event handlers will become functional once this code hits but the DOM should not change.

import {render, hydrate} from "react-dom" if (window.hasRestoredState) { hydrate(<MyPage />, document.getElementById('content'));
} else { render(<MyPage />, document.getElementById('content'));
}

Step 6: Go Async all the way

Turn your script tags from sync to async/defer for loading the JavaScript files. This is another key step to ensure a smooth loading and rendering experience on the front end.

That’s it! Reload the page to see the boost in performance.

Measuring improvement

OK, so you did all that work and now you want to know just how performant your site is. You’re going to want to benchmark the improvements.

Render Caching shines in situations where you have multiple server calls before you know what to render. On script-heavy pages, JavaScript can actually take a lot of time to parse.

You can measure the load performance in the Performance tab in Chrome’s DevTools.

Measuring rendering in the Performance tab of Chrome’s DevTools

Ideally, you’d use a guest profile so that your browser extensions won’t interfere with the measurements. You should see a significant improvement on reload. In the screenshot above, we a sample app with an async data.json fetch call that is performed before calling ReactDOM.hydrate. With Render Caching, the render is complete even before the data is loaded!

Wrapping up

Render Caching is a clever technique to ensure that the perceived speed of re-fetches of the same web page is faster by adding a caching layer to the final HTML and showing them back to the user. Users who visit your site frequently are the ones who stand to benefit the most.

As you can see, we accomplished this with very little code and the performance gains we get in return are huge. Do try this out on your website and post your comments. I’d love to hear whether your site performance sees the same significant boosts that I’ve experienced.

The post Render Caching for React appeared first on CSS-Tricks.

Using feature detection to write CSS with cross-browser support

In early 2017, I presented a couple of workshops on the topic of CSS feature detection, titled CSS Feature Detection in 2017.

A friend of mine, Justin Slack from New Media Labs, recently sent me a link to the phenomenal Feature Query Manager extension (available for both Chrome and Firefox), by Nigerian developer Ire Aderinokun. This seemed to be a perfect addition to my workshop material on the subject.

However, upon returning to the material, I realized how much my work on the subject has aged in the last 18 months.

The CSS landscape has undergone some tectonic shifts:

  • The Atomic CSS approach, although widely hated at first, has gained some traction through libraries like Tailwind, and perhaps influenced the addition of several new utility classes to Bootstrap 4.
  • CSS-in-JS exploded in popularity, with Styled Components at the forefront of the movement.
  • The CSS Grid Layout spec has been adopted by browser vendors with surprising speed, and was almost immediately sanctioned as production ready.

The above prompted me to not only revisit my existing material, but also ponder the state of CSS feature detection in the upcoming 18 months.

In short:

  1. ❓ Why do we need CSS feature detection at all?
  2. 🛠️ What are good (and not so good) ways to do feature detection?
  3. 🤖 What does the future hold for CSS feature detection?

Cross-browser compatible CSS

When working with CSS, it seems that one of the top concerns always ends up being inconsistent feature support among browsers. This means that CSS styling might look perfect on my browsers of choice, but might be completely broken on another (perhaps an even more popular) browser.

Luckily, dealing with inconsistent browser support is trivial due to a key feature in the design of the CSS language itself. This behavior, called fault tolerance, means that browsers ignore CSS code they don’t understand. This is in stark contrast to languages like JavaScript or PHP that stop all execution in order to throw an error.

The critical implication here is that if we layer our CSS accordingly, properties will only be applied if the browser understands what they mean. As an example, you can include the following CSS rule and the browser will just ignore it— overriding the initial yellow color, but ignoring the third nonsensical value:

background-color: yellow;
background-color: blue; /* Overrides yellow */
background-color: aqy8godf857wqe6igrf7i6dsgkv; /* Ignored */

To illustrate how this can be used in practice, let me start with a contrived, but straightforward situation:

A client comes to you with a strong desire to include a call-to-action (in the form of a popup) on his homepage. With your amazing front-end skills, you are able to quickly produce the most obnoxious pop-up message known to man:

Unfortunately, it turns out that his wife has an old Windows XP machine running Internet Explorer 8. You’re shocked to learn that what she sees no longer resembles a popup in any shape or form.

But! We remember that by using the magic of CSS fault tolerance, we can remedy the situation. We identify all the mission-critical parts of the styling (e.g., the shadow is nice to have, but does not add anything useful usability-wise) and buffer prepend all core styling with fallbacks.

This means that our CSS now looks something like the following (the overrides are highlighted for clarity):

.overlay { background: grey; background: rgba(0, 0, 0, 0.4); border: 1px solid grey; border: 1px solid rgba(0, 0, 0, 0.4); padding: 64px; padding: 4rem; display: block; display: flex; justify-content: center; /* if flex is supported */ align-items: center; /* if flex is supported */ height: 100%; width: 100%;
} .popup { background: white; background-color: rgba(255, 255, 255, 1); border-radius: 8px; border: 1px solid grey; border: 1px solid rgba(0, 0, 0, 0.4); box-shadow: 0 7px 8px -4px rgba(0,0, 0, 0.2), 0 13px 19px 2px rgba(0, 0, 0, 0.14), 0 5px 24px 4px rgba(0, 0, 0, 0.12); padding: 32px; padding: 2rem; min-width: 240px;
} button { background-color: #e0e1e2; background-color: rgba(225, 225, 225, 1); border-width: 0; border-radius: 4px; border-radius: 0.25rem; box-shadow: 0 1px 3px 0 rgba(0,0,0,.2), 0 1px 1px 0 rgba(0,0,0,.14), 0 2px 1px -1px rgba(0,0,0,.12); color: #5c5c5c; color: rgba(95, 95, 95, 1); cursor: pointer; font-weight: bold; font-weight: 700; padding: 16px; padding: 1rem;
} button:hover { background-color: #c8c8c8; background-color: rgb(200,200,200); }

The above example generally falls under the broader approach of Progressive Enhancement. If you’re interested in learning more about Progressive Enhancement check out Aaron Gustafson’s second edition of his stellar book on the subject, titled Adaptive Web Design: Crafting Rich Experiences with Progressive Enhancement (2016).

If you’re new to front-end development, you might wonder how on earth does one know the support level of specific CSS properties. The short answer is that the more you work with CSS, the more you will learn these by heart. However, there are a couple of tools that are able to help us along the way:

  • Can I Use is a widely used directory that contains searchable, up to date support matrices for all CSS features.
  • Stylelint has a phenomenal plugin-called called No Unsupported Browser Features that gives scours errors for unsupported CSS (defined via Browserslist) either in your editor itself or via a terminal command.
  • There are several tools like BrowserStack or Cross Browser Testing that allow you to remotely test your website on different browsers. Note that these are paid services, although BrowserStack has a free tier for open source projects.

Even with all the above at our disposal, learning CSS support by heart will help us plan our styling up front and increase our efficiency when writing it.

Limits of CSS fault tolerance

The next week, your client returns with a new request. He wants to gather some feedback from users on the earlier changes that were made to the homepage—again, with a pop-up:

Once again it will look as follows in Internet Explorer 8:

Being more proactive this time, you use your new fallback skills to establish a base level of styling that works on Internet Explorer 8 and progressive styling for everything else. Unfortunately, we still run into a problem…

In order to replace the default radio buttons with ASCII hearts, we use the ::before pseudo-element. However this pseudo-element is not supported in Internet Explorer 8. This means that the heart icon does not render; however the display: none property on the <input type="radio"> element still triggers on Internet Explorer 8. The implication being that neither the replacement behavior nor the default behavior is shown.

Credit to John Faulds for pointing out that it is actually possible to get the ‘::before’ pseudo-element to work in Internet Explorer 8 if you replace the official double colon syntax with a single colon.

In short, we have a rule (display: none) whose execution should not be bound to its own support (and thus its own fallback structure), but to the support level of a completely separate CSS feature (::before).

For all intents and purposes, the common approach is to explore whether there are more straightforward solutions that do not rely on ::before. However, for the sake of this example, let’s say that the above solution is non-negotiable (and sometimes they are).

Enter User Agent Detection

A solution might be to determine what browser the user is using and then only apply display: none if their browser supports the ::before pseudo-element.

In fact, this approach is almost as old as the web itself. It is known as User Agent Detection or, more colloquially, browser sniffing.

It is usually done as follows:

  • All browsers add a JavaScript property on the global window object called navigator and this object contains a userAgent string property.
  • In my case, the userAgent string is: Mozilla/5.0 (Windows NT10.0;Win64;x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.9 Safari/537.36.
  • Mozilla Developer Network has a comprehensive list of how the above can be used to determine the browser.
  • If we are using Chrome, then the following should return true: (navigator.userAgent.indexOf("chrome") !== -1).
  • However, under the Internet Explorer section on MDN, we just get Internet Explorer. IE doesn’t put its name in the BrowserName/VersionNumber format.
  • Luckily, Internet Explorer provides its own native detection in the form of Conditional Comments.

This means that adding the following in our HTML should suffice:

<!--[if lt IE 9]> <style> input { display: block; } </style>
<![endif]-->

This means that the above will be applied, should the browser be a version of Internet Explorer lower than version 9 (IE 9 supports ::before)—effectively overriding the display: none property.
Seems straightforward enough?

Unfortunately, over time, some critical flaws emerged in User Agent Detection. So much so that Internet Explorer stopped supporting Conditional Comments from version 10 onward. You will also notice that in the Mozilla Developer Network link itself, the following is presented in an orange alert:

It’s worth re-iterating: it’s very rarely a good idea to use user agent sniffing. You can almost always find a better, more broadly compatible way to solve your problem!

The biggest drawback of User Agent Detection is that browser vendors started spoofing their user agent strings over time due to the following:

  • Developer adds CSS feature that is not supported in the browser.
  • Developer adds User Agent Detection code to serve fallbacks to the browser.
  • Browser eventually adds support for that specific CSS feature.
  • Original User Agent Detection code is not updated to take this into consideration.
  • Code always displays the fallback, even if the browser now supports the CSS feature.
  • Browser uses a fake user agent string to give users the best experience on the web.

Furthermore, even if we were able to infallibly determine every browser type and version, we would have to actively maintain and update our User Agent Detection to reflect the feature support state of those browsers (notwithstanding browsers that have not even been developed yet).

It is important to note that although there are superficial similarities between feature detection and User Agent Detection, feature detection takes a radically different approach than User Agent Detection. According to the Mozilla Developer Network, when we use feature detection, we are essentially doing the following:

  1. 🔎 Testing whether a browser is actually able to run a specific line (or lines) of HTML, CSS or JavaScript code.
  2. 💪 Taking a specific action based on the outcome of this test.

We can also look to Wikipedia for a more formal definition (emphasis mine):

Feature detection (also feature testing) is a technique used in web development for handling differences between runtime environments (typically web browsers or user agents), by programmatically testing for clues that the environment may or may not offer certain functionality. This information is then used to make the application adapt in some way to suit the environment: to make use of certain APIs, or tailor for a better user experience.

While a bit esoteric, this definition does highlight two important aspects of feature detection:

  • Feature detection is a technique, as opposed to a specific tool or technology. This means that there are various (equally valid) ways to accomplish feature detection.
  • Feature detection programmatically tests code. This means that browsers actually run a piece of code to see what happens, as opposed to merely using inference or comparing it against a theoretical reference/list as done with User Agent Detection.

CSS feature detection with @supports

The core concept is not to ask “What browser is this?” It’s to ask “Does your browser support the feature I want to use?”.

—Rob Larson, The Uncertain Web: Web Development in a Changing Landscape (2014)

Most modern browsers support a set of native CSS rules called CSS conditional rules. These allow us to test for certain conditions within the stylesheet itself. The latest iteration (known as module level 3) is described by the Cascading Style Sheets Working Group as follows:

This module contains the features of CSS for conditional processing of parts of style sheets, conditioned on capabilities of the processor or the document the style sheet is being applied to. It includes and extends the functionality of CSS level 2 [CSS21], which builds on CSS level 1 [CSS1]. The main extensions compared to level 2 are allowing nesting of certain at-rules inside ‘@media’, and the addition of the ‘@supports’ rule for conditional processing.

If you’ve used @media, @document or @import before, then you already have experience working with CSS conditional rules. For example when using CSS media queries we do the following:

  • Wrap a single or multiple CSS declarations in a code block with curly brackets, { }.
  • Prepend the code block with a @media query with additional information.
  • Include an optional media type. This can either be all, print, speech or the commonly used screen type.
  • Chain expressions with and/or to determine the scope. For example, if we use (min-width: 300px) and (max-width: 800px), it will trigger the query if the screen size is wider than 300 pixels and smaller than 800 pixels.

The feature queries spec (editor’s draft) prescribes behavior that is conveniently similar to the above example. Instead of using a query expression to set a condition based on the screen size, we write an expression to scope our code block according to a browser’s CSS support (emphasis mine):

The ‘@supports rule allows CSS to be conditioned on implementation support for CSS properties and values. This rule makes it much easier for authors to use new CSS features and provide good fallback for implementations that do not support those features. This is particularly important for CSS features that provide new layout mechanisms, and for other cases where a set of related styles needs to be conditioned on property support.

In short, feature queries are a small built-in CSS tool that allow us to only execute code (like the display: none example above) when a browser supports a separate CSS feature—and much like media queries, we are able to chain expressions as follows: @supports (display: grid) and ((animation-name: spin) or (transition: transform(rotate(360deg)).

So, theoretically, we should be able to do the following:

@supports (::before) { input { display: none; }
}

Unfortunately, it seems that in our example above the display: none property did not trigger, in spite of the fact that your browser probably supports ::before.

That’s because there are some caveats to using @supports:

  • First and foremost, CSS feature queries only support CSS properties and not CSS pseudo-element, like ::before.
  • Secondly, you will see that in the above example our @supports (transform: scale(2)) and (animation-name: beat) condition fires correctly. However if we were to test it in Internet Explorer 11 (which supports both transform: scale(2) and animation-name: beat) it does not fire. What gives? In short, @supports is a CSS feature, with a support matrix of its own.

CSS feature detection with Modernizr

Luckily, the fix is fairly easy! It comes in the form of an open source JavaScript library named Modernizr, initially developed by Faruk Ateş (although it now has some pretty big names behind it, like Paul Irish from Chrome and Alex Sexton from Stripe).

Before we dig into Modernizr, let’s address a subject of great confusion for many developers (partly due to the name “Modernizr” itself). Modernizr does not transform your code or magically enable unsupported features. In fact, the only change Modernzr makes to your code is appending specific CSS classes to your <html> tag.

This means that you might end up with something like the following:

<html class="js flexbox flexboxlegacy canvas canvastext webgl no-touch geolocation postmessage websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients cssreflections csstransforms csstransforms3d csstransitions fontface generatedcontent video audio localstorage sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths">

That is one big HTML tag! However, it allows us do something super powerful: use the CSS descendant selector to conditionally apply CSS rules.

When Modernizr runs, it uses JavaScript to detect what the user’s browser supports, and if it does support that feature, Modernizr injects the name of it as a class to the <html>. Alternatively, if the browser does not support the feature, it prefixes the injected class with no- (e.g., no-generatedcontent in our ::before example). This means that we can write our conditional rule in the stylesheet as follows:

.generatedcontent input { display: none
}

In addition, we are able to replicate the chaining of @supports expressions in Modernizr as follows:

/* default */
.generatedcontent input { } /* 'or' operator */
.generatedcontent input, .csstransforms input { } /* 'and' operator */
.generatedcontent.csstransformsinput { } /* 'not' operator */
.no-generatedcontent input { }

Since Modernizr runs in JavaScript (and does not use any native browser APIs), it’s effectively supported on almost all browsers. This means that by leveraging classes like generatedcontent and csstransforms, we are able to cover all our bases for Internet Explorer 8, while still serving bleeding-edge CSS to the latest browsers.

It is important to note that since the release of Modernizr 3.0, we are no longer able to download a stock-standard modernizr.js file with everything except the kitchen sink. Instead, we have to explicitly generate our own custom Modernizr code via their wizard (to copy or download). This is most likely in response to the increasing global focus on web performance over the last couple of years. Checking for more features contributes to more loading, so Modernizr wants us to only check for what we need.

So, I should always use Modernizr?

Given that Modernizr is effectively supported across all browsers, is there any point in even using CSS feature queries? Ironically, I would not only say that we should but that feature queries should still be our first port of call.

First and foremost, the fact that Modernizr does not plug directly into the browser API is it’s greatest strength—it does not rely on the availability of a specific browser API. However, this benefit comes a cost, and that cost is additional overhead to something most browsers support out of the box through @supports—especially when you’re delivering this additional overhead to all users indiscriminately in order to a small amount of edge users. It is important to note that, in our example above, Internet Explorer 8 currently only stands at 0.18% global usage).

Compared to the light touch of @supports, Modernizr has the following drawbacks:

  • The approach underpinning development of Modernizr is driven by the assumption that Modernizr was “meant from day one to eventually become unnecessary.”
  • In the majority of cases, Modernizr needs to be render blocking. This means that Modernizr needs to be downloaded and executed in JavaScript before a web page can even show content on the screen—increasing our page load time (especially on mobile devices)!
  • In order to run tests, Modernizr often has to actually build hidden HTML nodes and test whether it works. For example, in order to test for <canvas> support, Modernizr executes the follow JavaScript code: return !!(document.createElement('canvas').getContext && document.createElement('canvas').getContext('2d'));. This consumes CPU processing power that could be used elsewhere.
  • The CSS descendant selector pattern used by Modernizr increases CSS specificity. (See Harry Roberts’ excellent article on why “specificity is a trait best avoided.”)
  • Although Modernizr covers a lot of tests (150+), it still does not cover the entire spectrum of CSS properties like @support does. The Modernizr team actively maintains a list of these undetectables.

Given that feature queries have already been widely implemented across the browser landscape, (covering about 93.42% of global browsers at the time of writing), it’s been a good while since I’ve used Modernizr. However, it is good to know that it exists as an option should we run into the limitations of @supports or if we need to support users still locked into older browsers or devices for a variety of potential reasons.

Furthermore, when using Modernizr, it is usually in conjunction with @supports as follows:

.generatedcontent input { display: none;
} label:hover::before { color: #c6c8c9;
} input:checked + label::before { color: black;
} @supports (transform: scale(2)) and (animation-name: beat) { input:checked + label::before { color: #e0e1e2; animation-name: beat; animation-iteration-count: infinite; animation-direction: alternate; }
}

This triggers the following to happen:

  • If ::before is not supported, our CSS will fallback to the default HTML radio select.
  • If neither transform(scale(2)) nor animation-name: beat are supported but ::before is, then the heart icon will change to black instead of animate when selected.
  • If transform(scale(2), animation-name: beat and ::before are supported, then the heart icon will animate when selected.

The future of CSS feature detection

Up until this point, I’ve shied away from talking about feature detection in a world being eaten by JavaScript, or possibly even a post-JavaScript world. Perhaps even intentionally so, since current iterations at the intersection between CSS and JavaScript are extremely contentious and divisive.

From that moment on, the web community was split in two by an intense debate between those who see CSS as an untouchable layer in the “separation of concerns” paradigm (content + presentation + behaviour, HTML + CSS + JS) and those who have simply ignored this golden rule and found different ways to style the UI, typically applying CSS styles via JavaScript. This debate has become more and more intense every day, bringing division in a community that used to be immune to this kind of “religion wars”.

—Cristiano Rastelli, Let there be peace on CSS (2017)

However, I think exploring how to apply feature detection in the modern CSS-in-JS toolchain might be of value as follows:

  • It provides an opportunity to explore how CSS feature detection would work in a radically different environment.
  • It showcases feature detection as a technique, as opposed to a specific technology or tool.

With this in mind, let us start by examining an implementation of our pop-up by means of the most widely-used CSS-in-JS library (at least at the time of writing), Styled Components:

This is how it will look in Internet Explorer 8:

In our previous examples, we’ve been able to conditionally execute CSS rules based on the browser support of ::before (via Modernizr) and transform (via @supports). However, by leveraging JavaScript, we are able to take this even further. Since both @supports and Modernizr expose their APIs via JavaScript, we are able to conditionally load entire parts of our pop-up based solely on browser support.

Keep in mind that you will probably need to do a lot of heavy lifting to get React and Styled Components working in a browser that does not even support ::before (checking for display: grid might make more sense in this context), but for the sake of keeping with the above examples, let us assume that we have React and Styled Components running in Internet Explorer 8 or lower.

In the example above, you will notice that we’ve created a component, called ValueSelection. This component returns a clickable button that increments the amount of likes on click. If you are viewing the example on a slightly older browser, you might notice that instead of the button you will see a dropdown with values from 0 to 9.

In order to achieve this, we’re conditionally returning an enhanced version of the component only if the following conditions are met:

if ( CSS.supports('transform: scale(2)') && CSS.supports('animation-name: beat') && Modernizr.generatedcontent
) { return ( <React.Fragment> <Modern type="button" onClick={add}>{string}</Modern> <input type="hidden" name="liked" value={value} /> </React.Fragment> )
} return ( <Base value={value} onChange={select}> { [1,2,3,4,5,6,7,8,9].map(val => ( <option value={val} key={val}>{val}</option> )) } </Base>
);

What is intriguing about this approach is that the ValueSelection component only exposes two parameters:

  • The current amount of likes
  • The function to run when the amount of likes are updated
<Overlay> <Popup> <Title>How much do you like popups?</Title> <form> <ValueInterface value={liked} change={changeLike} /> <Button type="submit">Submit</Button> </form> </Popup>
</Overlay>

In other words, the component’s logic is completely separate from its presentation. The component itself will internally decide what presentation works best given a browser’s support matrix. Having the conditional presentation abstracted away inside the component itself opens the door to exciting new ways of building cross-browser compatible interfaces when working in a front-end and/or design team.

Here’s the final product:

…and how it should theoretically look in Internet Explorer 8:

Additional Resources

If you are interested in diving deeper into the above you can visit the following resources:

  • Mozilla Developer Network article on feature detection
  • Mozilla Developer Network article on user agent detection
  • Mozilla Developer Network article on CSS feature queries
  • Official feature queries documentation by the CSSWG
  • Modernizr Documentation

Schalk is a South African front-end developer/designer passionate about the role technology and the web can play as a force for good in his home country. He works full time with a group of civic tech minded developers at a South African non-profit called OpenUp.

He also helps manage a collaborative space called Codebridge where developers are encouraged to come and experiment with technology as a tool to bridge social divides and solve problems alongside local communities.

The post Using feature detection to write CSS with cross-browser support appeared first on CSS-Tricks.

Working with refs in React

Refs make it possible to access DOM nodes directly within React. This comes in handy in situations where, just as one example, you want to change the child of a component. Let’s say you want to change the value of an <input> element, but without using props or re-rendering the whole component.

That’s the sort of thing refs are good for and what we’ll be digging into in this post.

How to create a ref

createRef() is a new API that shipped with React 16.3. You can create a ref by calling React.createRef() and attaching a React element to it using the ref attribute on the element.

class Example extends React.Component { constructor(props) { super(props) // Create the ref this.exampleRef = React.createRef() } render() { return ( <div> // Call the ref with the `ref` attribute <input type="text" ref={this.exampleRef} /> </div> ) }
}

We can “refer” to the node of the ref created in the render method with access to the current attribute of the ref. From the example above, that would be this.exampleRef.current.

Here’s an example:

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

class App extends React.Component { constructor(props) { super(props) // Create the ref this.textInput = React.createRef(); this.state = { value: '' } } // Set the state for the ref handleSubmit = e => { e.preventDefault(); this.setState({ value: this.textInput.current.value}) }; render() { return ( <div> <h1>React Ref - createRef</h1> // This is what will update <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> // Call the ref on <input> so we can use it to update the <h3> value <input type="text" ref={this.textInput} /> <button>Submit</button> </form> </div> ); }
}
How a conversation between a child component and an element containing the ref might go down.

This is a component that renders some text, an input field and a button. The ref is created in the constructor and then attached to the input element when it renders. When the button is clicked, the value submitted from the input element (which has the ref attached) is used to update the state of the text (contained in an H3 tag). We make use of this.textInput.current.value to access the value and the new state is then rendered to the screen.

Passing a callback function to ref

React allows you to create a ref by passing a callback function to the ref attribute of a component. Here is how it looks:

<input type="text" ref={element => this.textInput = element} />

The callback is used to store a reference to the DOM node in an instance property. When we want to make use of this reference, we access it using:

this.textInput.value

Let’s see how that looks in the same example we used before.

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

class App extends React.Component { state = { value: '' } handleSubmit = e => { e.preventDefault(); this.setState({ value: this.textInput.value}) }; render() { return ( <div> <h1>React Ref - Callback Ref</h1> <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> <input type="text" ref={element => this.textInput = element} /> <button>Submit</button> </form> </div> ); }
}

When you make use of callback like we did above, React will call the ref callback with the DOM node when the component mounts, when the component un-mounts, it will call it with null.

It is also possible to pass ref from a parent component to a child component using callbacks.

See the Pen React Ref – Callback Ref 2 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Let’s create our “dumb” component that will render a simple input:

const Input = props => { return ( <div> <input type="text" ref={props.inputRef} /> </div> );
};

This component is expecting inputRef props from its parent component which is then used to create a ref to the DOM node.

Here’s the parent component:

class App extends React.Component { state = { value: '' }; handleSubmit = event => { this.setState({ value: this.inputElement.value }); }; render() { return ( <div> <h1>React Ref - Callback Ref</h1> <h3>Value: {this.state.value}</h3> <Input inputRef={el => (this.inputElement = el)} /> <button onClick={this.handleSubmit}>Submit</button> </div> ); }
}

In the App component, we want to obtain the text that is entered in the input field (which is in the child component) so we can render it. The ref is created using a callback like we did in the first example of this section. The key lies in how we access the DOM of the input element in the Input component from the App component. If you look closely, we access it using this.inputElement. So, when updating the state of value in the App component, we get the text that was entered in the input field using this.inputElement.value.

The ref attribute as a string

This is the old way of creating a ref and it will likely be removed in a future release because of some issues associated with it. The React team advises against using it, going so far as to label it as “legacy” in the documentation. We’re including it here anyway because there’s a chance you could come across it in a codebase.

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

Going back to our example of an input whose value is used to update text value on submit:

class App extends React.Component { state = { value: '' } handleSubmit = e => { e.preventDefault(); this.setState({ value: this.refs.textInput.value}) }; render() { return ( <div> <h1>React Ref - String Ref</h1> <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> <input type="text" ref="textInput" /> <button>Submit</button> </form> </div> ); }
}

The component is initialized and we start with a default state value set to an empty string (value='’). The component renders the text and form, as usual and, like before, the H3 text updates its state when the form is submitted with the contents entered in the input field.

We created a ref by setting the ref prop of the input field to textInput. That gives us access to the value of the input in the handleSubmit() method using this.refs.textInput.value.

Forwarding a ref from one component to another

**Ref forwarding is the technique of passing a ref from a component to a child component by making use of the React.forwardRef() method.

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

Back to our running example of a input field that updates the value of text when submitted:

class App extends React.Component { constructor(props) { super(props) this.inputRef = React.createRef(); this.state = { value: '' } } handleSubmit = e => { e.preventDefault(); this.setState({ value: this.inputRef.current.value}) }; render() { return ( <div> <h1>React Ref - createRef</h1> <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> <Input ref={this.inputRef} /> <button>Submit</button> </form> </div> ); }
}

We’ve created the ref in this example with inputRef, which we want to pass to the child component as a ref attribute that we can use to update the state of our text.

const Input = React.forwardRef((props, ref) => ( <input type="text" ref={ref} />
));

Here is an alternative way to do it by defining the ref outside of the App component:

const Input = React.forwardRef((props, ref) => ( <input type="text" ref={ref} />
)); const inputRef = React.createRef(); class App extends React.Component { constructor(props) { super(props) this.state = { value: '' } } handleSubmit = e => { e.preventDefault(); this.setState({ value: inputRef.current.value}) }; render() { return ( <div> <h1>React Ref - createRef</h1> <h3>Value: {this.state.value}</h3> <form onSubmit={this.handleSubmit}> <Input ref={inputRef} /> <button>Submit</button> </form> </div> ); }
}

Using ref for form validation

We all know that form validation is super difficult but something React is well-suited for. You know, things like making sure a form cannot be submitted with an empty input value. Or requiring a password with at least six characters. Refs can come in handy for these types of situations.

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

class App extends React.Component { constructor(props) { super(props); this.username = React.createRef(); this.password = React.createRef(); this.state = { errors: [] }; } handleSubmit = (event) => { event.preventDefault(); const username = this.username.current.value; const password = this.password.current.value; const errors = this.handleValidation(username, password); if (errors.length > 0) { this.setState({ errors }); return; } // Submit data }; handleValidation = (username, password) => { const errors = []; // Require username to have a value on submit if (username.length === 0) { errors.push("Username cannot be empty"); } // Require at least six characters for the password if (password.length < 6) { errors.push("Password should be at least 6 characters long"); } // If those conditions are met, then return error messaging return errors; }; render() { const { errors } = this.state; return ( <div> <h1>React Ref Example</h1> <form onSubmit={this.handleSubmit}> // If requirements are not met, then display errors {errors.map(error => <p key={error}>{error}</p>)} <div> <label>Username:</label> // Input for username containing the ref <input type="text" ref={this.username} /> </div> <div> <label>Password:</label> // Input for password containing the ref <input type="text" ref={this.password} /> </div> <div> <button>Submit</button> </div> </form> </div> ); }
}

We used the createRef() to create refs for the inputs which we then passed as parameters to the validation method. We populate the errors array when either of the input has an error, which we then display to the user.

That’s a ref… er, a wrap!

Hopefully this walkthrough gives you a good understanding of how powerful refs can be. They’re an excellent way to update part of a component without the need to re-render the entire thing. That’s convenient for writing leaner code and getting better performance.

At the same time, it’s worth heeding the advice of the React docs themselves and avoid using ref too much:

Your first inclination may be to use refs to “make things happen” in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to “own” that state is at a higher level in the hierarchy.

Get it? Got it? Good.

The post Working with refs in React appeared first on CSS-Tricks.

Using data in React with the Fetch API and axios

If you are new to React, and perhaps have only played with building to-do and counter apps, you may not yet have run across a need to pull in data for your app. There will likely come a time when you’ll need to do this, as React apps are most well suited for situations where you’re handling both data and state.

The first set of data you may need to handle might be hard-coded into your React application, like we did for this demo from our Error Boundary tutorial:

See the Pen error boundary 0 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

What if you want to handle data from an API? That’s the purpose of this tutorial. Specifically, we’ll make use of the Fetch API and axios as examples for how to request and use data.

The Fetch API

The Fetch API provides an interface for fetching resources. We’ll use it to fetch data from a third-party API and see how to use it when fetching data from an API built in-house.

Using Fetch with a third-party API

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

We will be fetching random users from JSONPlaceholder, a fake online REST API for testing. Let’s start by creating our component and declaring some default state.

class App extends React.Component { state = { isLoading: true, users: [], error: null } render() { <React.Fragment> </React.Fragment> }
}

There is bound to be a delay when data is being requested by the network. It could be a few seconds or maybe a few milliseconds. Either way, during this delay, it’s good practice to let users know that something is happening while the request is processing.

To do that we’ll make use of isLoading to either display the loading message or the requested data. The data will be displayed when isLoading is false, else a loading message will be shown on the screen. So the render() method will look like this:

render() { const { isLoading, users, error } = this.state; return ( <React.Fragment> <h1>Random User</h1> // Display a message if we encounter an error {error ? <p>{error.message}</p> : null} // Here's our data check {!isLoading ? ( users.map(user => { const { username, name, email } = user; return ( <div key={username}> <p>Name: {name}</p> <p>Email Address: {email}</p> <hr /> </div> ); }) // If there is a delay in data, let's let the user know it's loading ) : ( <h3>Loading...</h3> )} </React.Fragment> );
}

The code is basically doing this:

  1. De-structures isLoading, users and error from the application state so we don’t have to keep typing this.state.
  2. Prints a message if the application encounters an error establishing a connection
  3. Checks to see if data is loading
  4. If loading is not happening, then we must have the data, so we display it
  5. If loading is happening, then we must still be working on it and display “Loading…” while the app is working

For Steps 3-5 to work, we need to make the request to fetch data from an API. This is where the JSONplaceholder API will come in handy for our example.

fetchUsers() { // Where we're fetching data from fetch(`https://jsonplaceholder.typicode.com/users`) // We get the API response and receive data in JSON format... .then(response => response.json()) // ...then we update the users state .then(data => this.setState({ users: data, isLoading: false, }) ) // Catch any errors we hit and update the app .catch(error => this.setState({ error, isLoading: false }));
}

We create a method called fetchUser() and use it to do exactly what you might think: request user data from the API endpoint and fetch it for our app. Fetch is a promise-based API which returns a response object. So, we make use of the json() method to get the response object which is stored in data and used to update the state of users in our application. We also need to change the state of isLoading to false so that our application knows that loading has completed and all is clear to render the data.

The fact that Fetch is promise-based means we can also catch errors using the .catch() method. Any error encountered is used a value to update our error’s state. Handy!

The first time the application renders, the data won’t have been received — it can take seconds. We want to trigger the method to fetch the users when the application state can be accessed for an update and the application re-rendered. React’s componentDidMount() is the best place for this, so we’ll place the fetchUsers() method in it.

componentDidMount() { this.fetchUsers();
}

Using Fetch With Self-Owned API

So far, we’ve looked at how to put someone else’s data to use in an application. But what if we’re working with our own data in our own API? That’s what we’re going to cover right now.

See the Pen React Fetch API Pen 2 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

I built an API which is available on GitHub. The JSON response you get has been placed on AWS — that’s what we will use for this tutorial.

As we did before, let’s create our component and set up some default state.

class App extends React.Component { state = { isLoading: true, posts: [], error: null } render() { <React.Fragment> </React.Fragment> }
}

Our method for looping through the data will be different from the one we used before but only because of the data’s structure, which is going to be different. You can see the difference between our data structure here and the one we obtained from JSONPlaceholder.

Here is how the render() method will look like for our API:

render() { const { isLoading, posts, error } = this.state; return ( <React.Fragment> <h1>React Fetch - Blog</h1> <hr /> {!isLoading ? Object.keys(posts).map(key => <Post key={key} body={posts[key]} />) : <h3>Loading...</h3>} </React.Fragment> );
}

Let’s break down the logic

{ !isLoading ? Object.keys(posts).map(key => <Post key={key} body={posts[key]} />) : <h3>Loading...</h3>
}

When isLoading is not true, we return an array, map through it and pass the information to the Post component as props. Otherwise, we display a “Loading…” message while the application is at work. Very similar to before.

The method to fetch posts will look like the one used in the first part.

fetchPosts() { // The API where we're fetching data from fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json`) // We get a response and receive the data in JSON format... .then(response => response.json()) // ...then we update the state of our application .then( data => this.setState({ posts: data, isLoading: false, }) ) // If we catch errors instead of a response, let's update the app .catch(error => this.setState({ error, isLoading: false }));
}

Now we can call the fetchPosts method inside a componentDidMount() method

componentDidMount() { this.fetchPosts();
}

In the Post component, we map through the props we received and render the title and content for each post:

const Post = ({ body }) => { return ( <div> {body.map(post => { const { _id, title, content } = post; return ( <div key={_id}> <h2>{title}</h2> <p>{content}</p> <hr /> </div> ); })} </div> );
};

There we have it! Now we know how to use the Fetch API to request data from different sources and put it to use in an application. High fives. ✋

axios

OK, so we’ve spent a good amount of time looking at the Fetch API and now we’re going to turn our attention to axios.

Like the Fetch API, axios is a way we can make a request for data to use in our application. Where axios shines is how it allows you to send an asynchronous request to REST endpoints. This comes in handy when working with the REST API in a React project, say a headless WordPress CMS.

There’s ongoing debate about whether Fetch is better than axios and vice versa. We’re not going to dive into that here because, well, you can pick the right tool for the right job. If you’re curious about the points from each side, you can read here and here.

Using axios with a third-party API

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

Like we did with the Fetch API, let’s start by requesting data from an API. For this one, we’ll fetch random users from the Random User API.

First, we create the App component like we’ve done it each time before:

class App extends React.Component { state = { users: [], isLoading: true, errors: null }; render() { return ( <React.Fragment> </React.Fragment> ); }
}

The idea is still the same: check to see if loading is in process and either render the data we get back or let the user know things are still loading.

To make the request to the API, we’ll need to create a function. We’ll call the function getUsers(). Inside it, we’ll make the request to the API using axios. Let’s see how that looks like before explaining further.

getUsers() { // We're using axios instead of Fetch axios // The API we're requesting data from .get("https://randomuser.me/api/?results=5") // Once we get a response, we'll map the API endpoints to our props .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}` })) ) // Let's make sure to change the loading state to display the data .then(users => { this.setState({ users, isLoading: false }); }) // We can still use the `.catch()` method since axios is promise-based .catch(error => this.setState({ error, isLoading: false }));
}

Quite different from the Fetch examples, right? The basic structure is actually pretty similar, but now we’re in the business of mapping data between endpoints.

The GET request is passed from the API URL as a parameter. The response we get from the API contains an object called data and that contains other objects. The information we want is available in data.results, which is an array of objects containing the data of individual users.

Here we go again with calling our method inside of the componentDidMount() method:

componentDidMount() { this.getUsers();
}

Alternatively, you can do this instead and basically combine these first two steps:

componentDidMount() { axios .get("https://randomuser.me/api/?results=5") .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(users => { this.setState({ users, isLoading: false }); }) .catch(error => this.setState({ error, isLoading: false }));
}

If you are coding locally from your machine, you can temporarily edit the getUsers() function to look like this:

getUsers() { axios .get("https://randomuser.me/api/?results=5") .then(response => console.log(response)) .catch(error => this.setState({ error, isLoading: false }));
}

Your console should get something similar to this:

We map through the results array to obtain the information we need for each user. The array of users is then used to set a new value for our users state. With that done, we can then change the value of isLoading.

By default, isLoading is set to true. When the state of users is updated, we want to change the value of isLoading to false since this is the cue our app is looking for to make the switch from “Loading…” to rendered data.

render() { const { isLoading, users } = this.state; return ( <React.Fragment> <h2>Random User</h2> <div> {!isLoading ? ( users.map(user => { const { username, name, email, image } = user; return ( <div key={username}> <p>{name}</p> <div> <img src={image} alt={name} /> </div> <p>{email}</p> <hr /> </div> ); }) ) : ( <p>Loading...</p> )} </div> </React.Fragment> );
}

If you log the users state to the console, you will see that it is an array of objects:

The empty array shows the value before the data was obtained. The returned data contains only the name, username, email address and image of individual users because those are the endpoints we mapped out. There is a lot more data available from the API, of course, but we’d have to add those to our getUsers method.

Using axios with your own API

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

You have seen how to use axios with a third-party API but we can look at what it’s like to request data from our own API, just like we did with the Fetch API. In fact, let’s use same JSON file we used for Fetch so we can see the difference between the two approaches.

Here is everything put together:

class App extends React.Component { // State will apply to the posts object which is set to loading by default state = { posts: [], isLoading: true, errors: null }; // Now we're going to make a request for data using axios getPosts() { axios // This is where the data is hosted .get("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json") // Once we get a response and store data, let's change the loading state .then(response => { this.setState({ posts: response.data.posts, isLoading: false }); }) // If we catch any errors connecting, let's update accordingly .catch(error => this.setState({ error, isLoading: false })); } // Let's our app know we're ready to render the data componentDidMount() { this.getPosts(); } // Putting that data to use render() { const { isLoading, posts } = this.state; return ( <React.Fragment> <h2>Random Post</h2> <div> {!isLoading ? ( posts.map(post => { const { _id, title, content } = post; return ( <div key={_id}> <h2>{title}</h2> <p>{content}</p> <hr /> </div> ); }) ) : ( <p>Loading...</p> )} </div> </React.Fragment> ); }
}

The main difference between this method and using axios to fetch from a third-party is how the data is formatted. We’re getting straight-up JSON this way rather than mapping endpoints.

The posts data we get from the API is used to update the value of the component’s posts state. With this, we can map through the array of posts in render(). We then obtain the id, title and content of each post using ES6 de-structuring, which is then rendered to the user.

Like we did before, what is displayed depends on the value of isLoading. When we set a new state for posts using the data obtained from the API, we had to set a new state for isLoading, too. Then we can finally let the user know data is loading or render the data we’ve received.

async and await

Another thing the promise-based nate of axios allows us to do is take advantage of is async and await . Using this, the getPosts() function will look like this.

async getPosts() { const response = await axios.get("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json"); try { this.setState({ posts: response.data.posts, isLoading: false }); } catch (error) { this.setState({ error, isLoading: false }); }
}

Base instance

With axios, it’s possible to create a base instance where we drop in the URL for our API like so:

const api = axios.create({ baseURL: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json"
});

…then make use of it like this:

async getPosts() { const response = await api.get(); try { this.setState({ posts: response.data.posts, isLoading: false }); } catch (error) { this.setState({ error, isLoading: false }); }
}

Simply a nice way of abstracting the API URL.

Now, data all the things!

As you build React applications, you will run into lots of scenarios where you want to handle data from an API. Hopefully you know feel armed and ready to roll with data from a variety of sources with options for how to request it.

Want to play with more data? Sarah recently wrote up the steps for creating your own serverless API from a list of public APIs.

The post Using data in React with the Fetch API and axios appeared first on CSS-Tricks.

Finite State Machines with React

As JavaScript applications on the web have grown more complex, so too has the complexity of dealing with state in those applications — state being the aggregate of all the data that an application needs to perform its function. Over the last several years, there has been a ton of great innovation in the realm of state management through tools like Redux, MobX, and Vuex. Something that hasn’t gotten quite as much attention, though, is state design.

What in the heck do I mean by state design?

Let’s set the scene a little bit. In the past, when building an application that needs to fetch some data from a backend service and display it to the user, I’ve designed my state to use boolean flags for various things like isLoading, isSuccess, isError, and so on down the line. As this number of boolean flags grows, though, the number of possible states that my application can have grows exponentially with it — significantly increasing the likelihood of a user encountering an unintentional or error state.

To solve this issue, I’ve spent the last several months exploring the use of finite state machines as a way to better design the state of my applications.

Finite state machines are a mathematical model of computation, initially developed in the early 1940s, that have been used for decades to build both hardware and software for a wide array of technologies.

A finite state machine can be defined as any abstract machine that exists in exactly one of a finite number of states at a given time. In more practical terms, though, a state machine is characterized by a list of states with each state defining a finite, deterministic set of states that can be transitioned to by a given action.

Due to this finite and deterministic nature, we can use state diagrams to visualize our application — before or after it’s been built.

For example, if we wanted to visualize an authentication workflow, we could have three overarching states that our application could be in for a user: logged in, logged out, or loading.

A state diagram showing an application going from logged out, to loading, to logged in.

State machines, owing to their predictability, are especially popular in applications where reliability is critical — such as aviation software, manufacturing, and even the NASA Space Launch System. They’ve been a mainstay in the game development community for decades, as well.

In this article, we’ll tackle building something that most applications on the web use: authentication. We’ll use the state diagram above to guide us.

Before we get started, though, let’s familiarize ourselves with some of the libraries and APIs we’ll be using to build this application.

React Context API

React 16.3 introduced a new, stable version of the Context API. If you’ve worked much with React in the past, you may be familiar with how data is passed from parent to child through props. When you have certain data that is needed by a variety of components, you can end up doing what’s known as prop drilling — passing data through multiple levels of the component tree to get the data to a component that needs it.

Context helps alleviate the pain of prop drilling by providing a way to share data between components without having to explicitly pass that data through the component tree, making it perfect for storing authentication data.

When we create context, we get a Provider and Consumer pair. The provider will act as the “smart,” stateful component that contains our state machine definition, and maintains a record of the current state of our application.

xstate

xstate is a JavaScript library for functional, stateless finite state machines and statecharts — it will provide us a nice, clean API for managing definitions and transitions through our states.

A stateless finite state machine library might sound a bit strange, but essentially what it means is that xstate only cares about the state and transition that you pass it — meaning it’s up to your application to keep track of its own current state.

xstate has a lot of features worth mentioning that we won’t cover much in this article (since we’ll only begin to scratch the surface on statecharts): hierarchical machines, parallel machines, history states, and guards, just to name a few.

The approach

So, now that we’ve had a little bit of an introduction to both Context and xstate, let’s talk about the approach we’ll be taking.

We’ll start by defining the context for our application, then creating a stateful <App /> component (our provider) that will contain our authentication state machine, along with information about the current user and a method for the user to logout.

To set the stage a bit, let’s take a quick look at a CodePen demo of what we’ll be building.

See the Pen Authentication state machine example by Jon Bellah (@jonbellah) on CodePen.

So, without further ado, let’s dig into some code!

Defining our context

The first thing we need to do is define our application context and set it up with some default values. Default values in context are helpful for allowing us to test components in isolation, since the default values are only used if there is no matching provider.

For our application, we’re going to set up a few defaults: authState which will be the authentication state of the current user, an object called user which will contain data about our user if they’re authenticated, then a logout() method that can be called anywhere in the app if the user is authenticated.

const Auth = React.createContext({ authState: 'login', logout: () => {}, user: {},
});

Defining our machine

When we think about how authentication behaves in an application, in its simplest form, there are three primary states: logged out, logged in, and loading. These are the three states we diagramed earlier.

Looking back at that state diagram, our machine consists of those same three states: logged out, logged in, and loading. We also have four different action types that can be fired: SUBMIT, SUCCESS, FAIL, and LOGOUT.

We can model that behavior in code like so:

const appMachine = Machine({ initial: 'loggedOut', states: { loggedOut: { onEntry: ['error'], on: { SUBMIT: 'loading', }, }, loading: { on: { SUCCESS: 'loggedIn', FAIL: 'loggedOut', }, }, loggedIn: { onEntry: ['setUser'], onExit: ['unsetUser'], on: { LOGOUT: 'loggedOut', }, }, },
});

So, we just expressed the diagram from earlier in code, but are you ready for me to let you in on a little secret? That diagram was generated from this code using David Khourshid’s xviz library — which can be used to visually explore the actual code that powers your state machines.

If you’re interested in diving deeper into complex user interfaces using finite state machines, David Khourshid has a related article here on CSS-Tricks worth checking out.

This can be an incredibly powerful tool when trying to debug problematic states in your application.

Referring back to the code above now, we define our initial application state — which we’re calling loggedOut since we’ll want to show the login screen on an initial visit.

Note that in a typical application, you’d probably want to start from the loading state and determine if the user was previously authenticated… but since we’re faking the login process, we’re starting from the logged out state.

In the states object, we define each of our states along with the corresponding actions and transitions for each of those states. Then we pass all that as an object to the Machine() function, which is imported from xstate.

Along with our loggedOut and loggedIn states, we’ve defined some actions that we want to fire when our application enters or exits those states. We’ll look at what those actions do in a bit.

This is our state machine.

To break things down one more time, let’s look at the loggedOut: { on: { SUBMIT: 'loading'} } line . This means that if our application is in the loggedOut state and we call our transition function with an action of SUBMIT, our application will always transition from the loggedOut state to the loading state. We can make that transition by calling appMachine.transition('loggedOut', 'SUBMIT').

From there, the loading state will either move the user along as an authenticated user or send them back to the login screen and display an error message.

Creating our context provider

The context provider will be the component that sits at the top level of our application and houses all the data related to an authenticated — or unauthenticated — user.

Working in the same file as our state machine definition, let’s create an <App /> component and set it up with everything we’ll need. Don’t worry, we’ll cover what each method does in just a moment.

class App extends React.Component { constructor(props) { super(props); this.state = { authState: appMachine.initialState.value, error: '', logout: e => this.logout(e), user: {}, }; } transition(event) { const nextAuthState = appMachine.transition(this.state.authState, event.type); const nextState = nextAuthState.actions.reduce( (state, action) => this.command(action, event) || state, undefined, ); this.setState({ authState: nextAuthState.value, ...nextState, }); } command(action, event) { switch (action) { case 'setUser': if (event.username) { return { user: { name: event.username } }; } break; case 'unsetUser': return { user: {}, }; case 'error': if (event.error) { return { error: event.error, }; } break; default: break; } } logout(e) { e.preventDefault(); this.transition({ type: 'LOGOUT' }); } render() { return ( <Auth.Provider value={this.state}> <div className="w5"> <div className="mb2">{this.state.error}</div> {this.state.authState === 'loggedIn' ? ( <Dashboard /> ) : ( <Login transition={event => this.transition(event)} /> )} </div> </Auth.Provider> ); }
}

Whew, that was a lot of code! Let’s break it down into manageable chunks by taking a look at each method of this class individually.

In the constructor(), we’re setting our component state to the initial state of our appMachine, as well as setting our logout function in state, so that it can be passed through our application context to any consumer that needs it.

In the transition() method, we’re doing a few important things. First, we’re passing our current application state and the event type or action to xstate, so we can determine our next state. Then, in nextState, we take any actions associated with that next state (which will be one of our onEntry or onExit actions) and run them through the command() method — then we take all of the results and set our new application state.

In the command() method, we have a switch statement that returns an object — depending on the action type — which we use to pass data into our application state. This way, once a user has authenticated, we can set relevant details about that user — username, email, id, etc. — into our context, making it available to any of our consumer components.

Finally, in our render() method, we’re actually defining our provider component and then passing all of our current state through the value props, which makes the state available to all of the components beneath it in the component tree. Then, depending on the state of our application, we’re rendering either the dashboard or the login form for the user.

In this case, we have a pretty flat component tree beneath our provider (Auth.Provider), but remember that context allows that value to be available to any component beneath our provider in the component tree, regardless of depth. So, for example, if we have a component nested three or four levels down and we want to display the current user name, we can just grab that out of context, rather than drilling it all the way down to that one component.

Creating context consumers

Now, let’s create some components that consume our application context. From these components, we can do all sorts of things.

We can start by building a login component for our application.

class Login extends Component { constructor(props) { super(props); this.state = { yourName: '', } this.handleInput = this.handleInput.bind(this); } handleInput(e) { this.setState({ yourName: e.target.value, }); } login(e) { e.preventDefault(); this.props.transition({ type: 'SUBMIT' }); setTimeout(() => { if (this.state.yourName) { return this.props.transition({ type: 'SUCCESS', username: this.state.yourName, }, () => { this.setState({ username: '' }); }); } return this.props.transition({ type: 'FAIL', error: 'Uh oh, you must enter your name!', }); }, 2000); } render() { return ( <Auth.Consumer> {({ authState }) => ( <form onSubmit={e => this.login(e)}> <label htmlFor="yourName"> <span>Your name</span> <input id="yourName" name="yourName" type="text" value={this.state.yourName} onChange={this.handleInput} /> </label> <input type="submit" value={authState === 'loading' ? 'Logging in...' : 'Login' } disabled={authState === 'loading' ? true : false} /> </form> )} </Auth.Consumer> ); }
}

Oh my! That was another big chunk of code, so let’s walk through each method again.

In the constructor(), we’re declaring our default state and binding the handleInput() method so that it references the proper this internally.

In handleInput(), we’re taking the value of our form field from our render() method and setting that value in state — this is referred to as a controlled form.

The login() method is where you would normally place your authentication logic. In the case of this app, we’re just faking a delay with setTimeout() and either authenticating the user — if they’ve provided a name — or returning an error if the field was left empty. Note that the transition() function that it calls is actually the one we defined in our <App /> component, which has been passed down via props.

Finally, our render() method displays our login form, but notice that the <Login /> component is also a context consumer. We’re using the authState context to determine whether or not to show our login button in a disabled, loading state.

Using context from deep in the component tree

Now that we’ve handled the creation of our state machine and a way for users to login to our application, we can now rely on having information about that user within any component nested under our <Dashboard /> component — since it will only ever be rendered if the user is logged in.

So let’s create a stateless component that grabs the username of the current authenticated user and displays a welcome message. Since we’re passing the logout() method to all of our consumers, we can also give the user the option of logging out from anywhere in the component tree.

const Dashboard = () => ( <Auth.Consumer> {({ user, logout }) => ( <div> <div>Hello {user.name}</div> <button onClick={e => logout(e)}> Logout </button> </div> )} </Auth.Consumer>
);

Building larger applications with statecharts

Using finite state machines with React doesn’t have to be limited to authentication, nor does it have to be limited to the context API.

Using statecharts, you can have hierarchical machines and/or parallel machines — meaning individual React components can have their own internal state machine, but still be connected to the overall state of your application.

In this article, we’ve primarily focused on using xstate directly with the native Context API; in larger applications, I highly recommend looking at react-automata, which provides a thin layer of abstraction over the top of xstate. react-automata has the added benefit of being able to automagically generate Jest tests for your components.

State machines and state management tools are not mutually exclusive

It’s easy to get confused by thinking you must either use, say, xstate or Redux; but it’s important to note that state machines are more of an implementation concept, concerned with how you design your state — not necessarily how you manage it.

In fact, state machines can be used with just about any un-opinionated state management tool. I encourage you to explore various approaches to determine what works best for you, your team, and your application(s).

In conclusion

These concepts can be extended to the real world without having to refactor your entire application. State machines are an excellent refactor target — meaning, next time you’re working on a component that is littered with boolean flags like isFetching and isError, consider refactoring that component to use a state machine.

As a front-end developer, I’ve found that I’m often fixing one of two categories of bugs: display-related issues or unexpected application states.

State machines make the second category virtually disappear.

If you’re interested in diving deeper into state machines, I’ve spent the last several months working on a course on finite state machines — if you sign up for the email list, you’ll receive a discount code when the course launches in August.

The post Finite State Machines with React appeared first on CSS-Tricks.

Render Children in React Using Fragment or Array Components

What comes to your mind when React 16 comes up? Context? Error Boundary? Those are on point. React 16 came with those goodies and much more, but In this post, we’ll be looking at the rendering power it also introduced — namely, the ability to render children using Fragments and Array Components.

These are new and really exciting concepts that came out of the React 16 release, so let’s look at them closer and get to know them.

Fragments

It used to be that React components could only return a single element. If you have ever tried to return more than one element, you know that you’ll will be greeted with this error: Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag. The way out of that is to make use of a wrapper div or span element that acts as the enclosing tag.

So instead of doing this:

class Countries extends React.Component { render() { return ( <li>Canada</li> <li>Australia</li> <li>Norway</li> <li>Mexico</li> ) }
}

…you have to add either an ol or ul tag as a wrapper on those list items:

class Countries extends React.Component { render() { return ( <ul> <li>Canada</li> <li>Australia</li> <li>Norway</li> <li>Mexico</li> </ul> ) }
}

Most times, this may not be the initial design you had for the application, but you are left with no choice but to compromise on this ground.

React 16 solves this with Fragments. This new features allows you to wrap a list of children without adding an extra node. So, instead of adding an additional element as a wrapper like we did in the last example, we can throw <React.Fragment> in there to do the job:

class Countries extends React.Component { render() { return ( <React.Fragment> <li>Canada</li> <li>Australia</li> <li>Norway</li> <li>Mexico</li> </React.Fragment> ) }
}

You may think that doesn’t make much difference. But, imagine a situation where you have a component that lists different items such as fruits and other things. These items are all components, and if you are making use of old React versions, the items in these individual components will have to be wrapped in an enclosing tag. Now, however, you can make use of fragments and do away with that unnecessary markup.

Here’s a sample of what I mean:

class Items extends React.Component { render() { return ( <React.Fragment> <Fruit /> <Beverages /> <Drinks /> </React.Fragment> ) }
}

We have three child components inside of the fragment and can now create a component for the container that wraps it. This is much more in line with being able to create components out of everything and being able to compile code with less cruft.

Fragment Shorthand

There is a shorthand syntax when working with Fragments, which you can use. Staying true to its fragment nature, the syntax is like a fragment itself, leaving only only empty brackets behind.

Going back to our last example:

class Fruit extends React.Component { render() { return ( <> <li>Apple</li> <li>Orange</li> <li>Blueberry</li> <li>Cherry</li> </> ) }
}

Question: Is a fragment better than a container div?

You may be looking for a good reason to use Fragments instead of the container div you have always been using. Dan Abramov answered the question on StackOverflow. To summarize:

  1. It’s a tiny bit faster and has less memory usage (no need to create an extra DOM node). This only has a real benefit on very large and/or deep trees, but application performance often suffers from death by a thousand cuts. This is one less cut.
  2. Some CSS mechanisms like flexbox and grid have a special parent-child relationship, and adding divs in the middle makes it harder to maintain the design while extracting logical components.
  3. The DOM inspector is less cluttered.

Keys in Fragments

When mapping a list of items, you still need to make use of keys the same way as before. For example, let’s say we want to pass a list of items as props from a parent component to a child component. In the child component, we want to map through the list of items we have and output each item as a separate entity. Here’s how that looks:

const preload = { "data" : [ { "name": "Reactjs", "url": "https://reactjs.org", "description": "A JavaScript library for building user interfaces", }, { "name": "Vuejs", "url": "https://vuejs.org", "description": "The Progressive JavaScript Framework", }, { "name": "Emberjs", "url": "https://www.emberjs.com", "description": "Ember.js is an open-source JavaScript web framework, based on the Model–view–viewmodel pattern" } ]
} const Frameworks = (props) => { return ( <React.Fragment> {props.items.data.map(item => ( <React.Fragment key={item.id}> <h2>{item.name}</h2> <p>{item.url}</p> <p>{item.description}</p> </React.Fragment> ))} </React.Fragment> )
} const App = () => { return ( <Frameworks items={preload} /> )
}

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

You can see that now, in this case, we are not making use of any divs in the Frameworks component. That’s the key difference!

Render Children Using an Array of Components

The second specific thing that came out of React 16 we want to look at is the ability to render multiple children using an array of components. This is a clear timesaver because it allows us to cram as many into a render instead of having to do it one-by-one.

Here is an example:

class Frameworks extends React.Component { render () { return ( [ <p>JavaScript:</p> <li>React</li>, <li>Vuejs</li>, <li>Angular</li> ] ) }
}

You can also do the same with a stateless functional component:

const Frontend = () => { return [ <h3>Front-End:</h3>, <li>Reactjs</li>, <li>Vuejs</li> ]
} const Backend = () => { return [ <h3>Back-End:</h3>, <li>Express</li>, <li>Restify</li> ]
} const App = () => { return [ <h2>JavaScript Tools</h2>, <Frontend />, <Backend /> ]
}

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

Conclusion

Like the Context API and Error Boundary feature that were introduced in React 16, rendering children components with Fragment and multiples of them with Array Components are two more awesome features you can start making use of as you build your application.

Have you started using these in a project? Let me know how in the comments so we can compare notes. 🙂

The post Render Children in React Using Fragment or Array Components appeared first on CSS-Tricks.

Handling Errors with Error Boundary

Thinking and building in React involves approaching application design in chunks, or components. Each part of your application that performs an action can and should be treated as a component. In fact, React is component-based and, as Tomas Eglinkas recently wrote, we should leverage that concept and err on the side of splitting any large chunking into smaller components.

Splitting inevitably introduces component hierarchies, which are good because they bloated components and architecture. However, things can begin to get complicated when an error occurs in a child component. What happens when the whole application crashes?! Seriously, React, why do the parent and sibling components have to pay for the sins of another component? Why?

Error Boundaries

React 16 came with a lot of goodies, one of which is error boundaries. Let’s consult the documentation and break down what it says about this gem because we can use it to spot errors where they occur and resolve them faster and with less headache!

Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.

That’s a lot of jargon but, like components, we can break it down into less complex chunks.

Error boundaries are React Components

This makes a lot of sense and useful because it’s a concept we have using all along. The difference is that juice was sprayed on it to make it different from a normal component. Still, don’t forget the basic idea that error boundaries are themselves React Components!

Error boundaries catch JavaScript errors anywhere in their child component tree

In case you have forgotten how children component tree work, here is an example:

<ParentComponent> <FirstChild> <FirstChildDaughter> </FirstChildDaughter> </FirstChild> <SecondChild> </SecondChild>
</ParentComponent>

We have two parent and three child components. According to what we have learned so far about error boundaries, we can replicate the above tree to:

<ErrorBoundaryComponent> <ParentComponent> <FirstChild> <FirstChildDaughter> </FirstChildDaughter> </FirstChild> <SecondChild> </SecondChild> </ParentComponent>
</ErrorBoundaryComponent>

By wrapping the whole tree up in an ErrorBoundaryComponent, we can catch any JavaScript errors that occur in its child components. Cool, right?

Error boundaries log those errors

When errors are caught, we want boundaries errors to do something with them, preferably something to tell us about the. Developers often make use of error logging platforms to monitor errors that occur on their software. With error boundaries, we can do the same.

Error boundaries display a fallback UI

Instead of displaying the whole annoying combo of reds in different shades, you can choose a customized user interface to display when an error occurs. That can come in super handy because it allows you to tailor errors in a style that makes it easier for you to read and scan. Super cool, right?

Like me, you’ll think that this means error boundaries will catch all JavaScript errors. Sadly, that’s not true. Here are errors that they will gracefully ignore:

  • Event handlers
  • Asynchronous code (e.g. setTimeout or requestAnimationFrame callbacks)
  • Server-side rendering
  • Errors thrown in the error boundary itself (rather than its children)

componentDidCatch()

The extra juice that makes a component an error boundary is componentDidCatch() — this is a lifecycle method that works like the JavaScript catch{} block, but for components. When an error is found in a child component, the error is handled by the closest error boundary. Only class components can be error boundaries.

componentDidCatch() accepts two parameters:

  • error: This is the error that was thrown
  • info: An object which contains a trace of where the error occurred

Error Boundary In Action

Say we are working on a feature that lists locations where conferences can be held. Something like this:

See the Pen error boundary 0 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

The application lists locations from the Location component and the individual locations are output as Location Cards. We take a little extra care to ensure the name of each location is rendered in uppercase for consistency. For this tutorial purpose, we will add an empty object to the list of locations.

class Location extends React.Component { state = { locations: [ { "name": "Ojo", "zone": "Lagos State", "region": "South West" }, { "name": "Ahiazu Mbaise", "zone": "Imo State", "region": "South East" }, { "name": "Akoko-Edo", "zone": "Edo State", "region": "South South" }, { "name": "Anka", "zone": "Zamfara State", "region": "North West" }, { "name": "Akwanga", "zone": "Nasarawa State", "region": "North Central" }, { } ] } render() { return ( <div> <div> <div> <h2>Locations</h2> </div> </div> <div> {this.state.locations .map(location => <LocationCard key={location.id} {...location} /> )} </div> </div> ) }
} const LocationCard = (props) => { return ( <div> <hr /> <p><b>Name:</b> {props.name.toUpperCase()}</p> <p><b>Zone:</b> {props.zone}</p> <p><b>Region:</b> {props.region}</p> <hr /> </div> )
} const App = () => ( <div> <Location /> </div>
) ReactDOM.render(<App />, document.getElementById("root"));

If you run this in the browser, you will see an error similar to this screenshot:

A screenshot of the Type Error providing the error message Cannot read property toUpperCase of undefined. Background color is tan and there is a block of code with a light red background indicating where the error is in the code base.

That’s not totally helpful, so let’s apply an error boundary to handle help us out. First, we’ll create an ErrorBoundary component:

class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false, error: null, info: null }; } componentDidCatch(error, info) { this.setState({ hasError: true, error: error, info: info }); } render() { if (this.state.hasError) { return ( <div> <h1>Oops, something went wrong :(</h1> <p>The error: {this.state.error.toString()}</p> <p>Where it occured: {this.state.info.componentStack}</p> </div> ); } return this.props.children; }
}

An initial state for hasError, error, and info is created. Then, the componentDidCatch() lifecycle method is added. If an error occurs in the constructor, render or lifecycle method of any of its children components, the hasError state will be changed to true. When this happens, the ErrorBoundary component renders and displays the error. But if there are no errors, the children of the ErrorBoundary component are rendered instead as we’d expect.

Next, we need to add both the ErrorBoundary and Location components to our main App component:

const App = () => ( <div> <ErrorBoundary> <Location /> </ErrorBoundary> </div>
)

See the Pen error boundary 2 by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

We don’t see that annoying TypeError UI anymore! Things are working!

There’s one little thing we can do to improve the app. If you check the code in the demo, you’ll see an empty object we added at the end. Is it possible to have the other credible locations render? Most definitely! Inside the Location component, we can wrap the LocationCard component with the ErrorBoundary component to scope error logging directly to the cards:

class Location extends React.Component { state = { locations: [ { "name": "Ojo", "zone": "Lagos State", "region": "South West" }, { "name": "Ahiazu Mbaise", "zone": "Imo State", "region": "South East" }, { "name": "Akoko-Edo", "zone": "Edo State", "region": "South South" }, { "name": "Anka", "zone": "Zamfara State", "region": "North West" }, { "name": "Akwanga", "zone": "Nasarawa State", "region": "North Central" }, { // Empty! } ] } render() { return ( <div> <div> <div> <h2>Locations</h2> </div> </div> <div> {this.state.locations .map(location => <ErrorBoundary> // Should render all locations, but the empty instance <LocationCard key={location.id} {...location} /> </ErrorBoundary> )} </div> </div> ) }
}

This time, the credible locations show, except the one that is empty. You can choose to wrap the whole component tree with an error boundary component once, or you can wrap different components at strategic places. The decision is up to you.

Wrapping Up

I encourage you to start making use of error boundaries in your applications. Similarly, it’s worth digging in a little deeper. For that, here are some issues in the React repo on Error Boundaries and event handles, go through them so you can see the current state of where things are at:

  • componentDidCatch is not getting called when there is an error in promise
  • Why are Error Boundaries not triggered for event handlers?

The post Handling Errors with Error Boundary appeared first on CSS-Tricks.

Managing State in React With Unstated

As your application becomes more complex, the management of state can become tedious. A component’s state is meant to be self-contained, which makes sharing state across multiple components a headache. Redux is usually the go-to library to manage state in React, however, depending on how complex your application is, you might not need Redux.

Unstated is an alternative that provides you with the functionality to manage state across multiple components with a Container class and Provider and Subscribe components. Let’s see Unstated in action by creating a simple counter and then look at a more advanced to-do application.

Using Unstated to Create a Counter

The code for the counter we’re making is available on GitHub:

View Repo

You can add Unstated to your application with Yarn:

yarn add unstated

Container

The container extends Unstated’s Container class. It is to be used only for state management. This is where the initial state will be initialized and the call to setState() will happen.

import { Container } from 'unstated' class CounterContainer extends Container { state = { count: 0 } increment = () => { this.setState({ count: this.state.count + 1 }) } decrement = () => { this.setState({ count: this.state.count - 1 }) }
} export default CounterContainer

So far, we’ve defined the Container (CounterContainer), set its starting state for count at the number zero and defined methods for adding and subtracting to the component’s state in increments and decrements of one.

You might be wondering why we haven’t imported React at this point. There is no need to import it into the Container since we will not be rendering JSX at all.

Events emitters will be used in order to call setState() and cause the components to re-render. The components that will make use of this container will have to subscribe to it.

Subscribe

The Subscribe component is used to plug the state into the components that need it. From here, we will be able to call the increment and decrement methods, which will update the state of the application and cause the subscribed component to re-render with the correct count. These methods will be triggered by a couple of buttons that contain events listeners to add or subtract to the count, respectively.

import React from 'react'
import { Subscribe } from 'unstated' import CounterContainer from './containers/counter' const Counter = () => { return ( <Subscribe to={[CounterContainer]}> {counterContainer => ( <div> <div> // The current count value Count: { counterContainer.state.count } </div> // This button will add to the count <button onClick={counterContainer.increment}>Increment</button> // This button will subtract from the count <button onClick={counterContainer.decrement}>Decrement</button> </div> )} </Subscribe> )
} export default Counter

The Subscribe component is given the CounterContainer in the form of an array to its to prop. This means that the Subscribe component can subscribe to more than one container, and all of the containers are passed to the to prop of the Subscribe component in an array.

The counterContainer is a function that receives an instance of each container the Subscribe component subscribes to.

With that, we can now access the state and the methods made available in the container.

Provider

We’ll make use of the Provider component to store the container instances and allow the children to subscribe to it.

import React, { Component } from 'react';
import { Provider } from 'unstated' import Counter from './Counter' class App extends Component { render() { return ( <Provider> <Counter /> </Provider> ); }
} export default App;

With this, the Counter component can make use of our counterContainer.

Unstated allows you to make use of all the functionality that React’s setState() provides. For example, if we want to increment the previous state by one three times with one click, we can pass a function to setState() like this:

incrementBy3 = () => { this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 })) this.setState((prevState) => ({ count: prevState.count + 1 }))
}

The idea is that the setState() still works like it does, but this time with the ability to keep the state contained in a Container class. It becomes easy to spread the state to only the components that need it.

Let’s Make a To-Do Application!

This is a slightly more advanced use of Unstated. Two components will subscribe to the container, which will manage all of the state, and the methods for updating the state. Again, the code is available on Github:

View Repo

The container will look like this:

import { Container } from 'unstated' class TodoContainer extends Container { state = { todos: [ 'Mess around with unstated', 'Start dance class' ], todo: '' }; handleDeleteTodo = (todo) => { this.setState({ todos: this.state.todos.filter(c => c !== todo) }) } handleInputChange = (event) => { const todo = event.target.value this.setState({ todo }); }; handleAddTodo = (event) => { event.preventDefault() this.setState(({todos}) => ({ todos: todos.concat(this.state.todo) })) this.setState({ todo: '' }); } } export default TodoContainer

The container has an initial todos state which is an array with two items in it. To add to-do items, we have a todo state set to an empty string.

We’re going to need a CreateTodo component that will subscribe to the container. Each time a value is entered, the onChange event will trigger then fire the handleInputChange() method we have in the container. Clicking the submit button will trigger handleAddTodo(). The handleDeleteTodo() method receives a to-do and filters out the to-do that matches the one passed to it.

import React from 'react'
import { Subscribe } from 'unstated' import TodoContainer from './containers/todoContainer' const CreateTodo = () => { return ( <div> <Subscribe to={[TodoContainer]}> {todos => <div> <form onSubmit={todos.handleAddTodo}> <input type="text" value={todos.state.todo} onChange={todos.handleInputChange} /> <button>Submit</button> </form> </div> } </Subscribe> </div> );
} export default CreateTodo

When a new to-do is added, the todos state made available in the container is updated. The list of todos is pulled from the container to the Todos component, by subscribing the component to the container.

import React from 'react';
import { Subscribe } from 'unstated'; import TodoContainer from './containers/todoContainer' const Todos = () => ( <ul> <Subscribe to={[TodoContainer]}> {todos => todos.state.todos.map(todo => ( <li key={todo}> {todo} <button onClick={() => todos.handleDeleteTodo(todo)}>X</button> </li> )) } </Subscribe> </ul>
); export default Todos

This component loops through the array of to-dos available in the container and renders them in a list.

Finally, we need to wrap the components that subscribe to the container in a provider like we did in the case of the counter. We do this in our App.js file exactly like we did in the counter example:

import React, { Component } from 'react';
import { Provider } from 'unstated' import CreateTodo from './CreateTodo'
import Todos from './Todos' class App extends Component { render() { return ( <Provider> <CreateTodo /> <Todos /> </Provider> ); }
} export default App;

Wrapping Up

There are different ways of managing state in React depending on the complexity of your application and Unstated is a handy library that can make it easier. It’s worth reiterating the point that Redux, while awesome, is not always the best tool for the job, even though we often grab for it in these types of cases. Hopefully you now feel like you have a new tool in your belt.

The post Managing State in React With Unstated appeared first on CSS-Tricks.

How React Reconciliation Works

React is fast! Some of that speed comes from updating only the parts of the DOM that need it. Less for you to worry about and a speed gain to boot. As long as you understand the workings of setState(), you should be good to go. However, it’s also important to familiarize yourself with how this amazing library updates the DOM of your application. Knowing this will be instrumental in your work as a React developer.

The DOM?

The browser builds the DOM by parsing the code you write, it does this before it renders the page. The DOM represents documents in the page as nodes and objects, providing an interface so that programming languages can plug in and manipulate the DOM. The problem with the DOM is that it is not optimized for dynamic state changes, React has to calculate if it is necessary to update the DOM. It does this by creating a virtual DOM and comparing it with the current DOM. In this context, the virtual DOM will contain the new state of the component.

Let’s build a simple component that adds two numbers. The numbers will be entered in an input field.

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

First, we’ll need to set up the initial state for the fields, then update the state when a number is entered. The component will look like this:

class App extends React.Component { state = { result: '', entry1: '', entry2: '' } handleEntry1 = (event) => { this.setState({entry1: event.target.value}) } handleEntry2 = (event) => { this.setState({entry2: event.target.value}) } handleAddition = (event) => { const firstInt = parseInt(this.state.entry1) const secondInt = parseInt(this.state.entry2) this.setState({result: firstInt + secondInt }) } render() { const { entry1, entry2, result } = this.state return( <div> <div> <p>Entry 1: { entry1 }</p> <p>Entry 2: { entry2 }</p> <p>Result: { result }</p> </div> <br /> <div> Entry 1:  <input type='text' onChange={this.handleEntry1} /> </div> <br /> <div> Entry 2:  <input type='text' onChange={this.handleEntry2} /> </div> <div> <button onClick={this.handleAddition} type='submit'>Add</button> </div> </div> ) }
}

On initial render, the DOM tree will look like this;

A screenshot from DevTools that shows the HTML rendering of the app.

When an entry is made in the first input field, React creates a new tree. The new tree which is the virtual DOM will contain the new state for entry1. Then, React compares the virtual DOM with the old DOM and, from the comparison, it figures out the difference between both DOMs and makes an update to only the part that is different. A new tree is created each time the state of App component changes — when a value is entered in either of the inputs field, or when the button is clicked.

Animated gif showing how the markup in DevTools changes when numbers are added to the input field.

Diffing Different Elements

When the state of a component changes so that an element needs to be changed from one type to another, React unmounts the whole tree and builds a new one from scratch. This causes every node in that tree to be destroyed.

Let’s see an example:

class App extends React.Component { state = { change: true } handleChange = (event) => { this.setState({change: !this.state.change}) } render() { const { change } = this.state return( <div> <div> <button onClick={this.handleChange}>Change</button> </div> { change ? <div> This is div cause it's true <h2>This is a h2 element in the div</h2> </div> : <p> This is a p element cause it's false <br /> This is another paragraph in the false paragraph </p> } </div> ) }
}

On initial render, you will see the div and its contents and how clicking the button causes React to destroy the div’s tree with its content and build a tree for the <p> element instead. Same happens if we have the same component in both cases. The component will be destroyed alongside the previous tree it belonged to, and a new instance will be built. See the demo below;

See the Pen reconciliation-2 Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Diffing Lists

React uses keys to keep track of items in a list. The keys help it figure out the position of the item on a list. What happens when a list does not have keys? React will mutate every child of the list even if there are no new changes.

In other words, React changes every item in a list that does not have keys.

Here’s an example:

const firstArr = ['codepen', 'codesandbox']
const secondArr = ['github', 'codepen', 'bitbucket', 'codesanbox'] class App extends React.Component { state = { change: true } handleChange = (event) => { this.setState({change: !this.state.change}) } render() { const { change } = this.state return( <div> <div> <button onClick={this.handleChange}>Change</button> </div> <ul> { change ? firstArr.map((e) => <li>{e}</li>) : secondArr.map((e) => <li>{e}</li>) } </ul> </div> ) }
}

Here, we have two arrays that get rendered depending on the state of the component. React has no way of keep track of the items on the list, so it is bound to change the whole list each time there is a need to re-render. This results in performance issues.

In your console, you will see a warning like this:

Warning: Each child in an array or iterator should have a unique "key" prop.

To fix this, you add a unique key for each item on the list.

const firstArr = ['codepen', 'codesandbox']
const secondArr = ['github', 'codepen', 'bitbucket', 'codesanbox'] class App extends React.Component { state = { change: true } handleChange = (event) => { this.setState({change: !this.state.change}) } render() { const { change } = this.state return( <div> <div> <button onClick={this.handleChange}>Change</button> </div> <ul> { change ? firstArr.map((e, index) => <li key={e.index}>{e}</li>) : secondArr.map((e, index) => <li key={e.index}>{e}</li>) } </ul> </div> ) }
}

See the Pen reconciliation-3 Pen by Kingsley Silas Chijioke (@kinsomicrote) on CodePen.

Wrapping Up

In summary, here are the two big takeaways for understanding how the concept of reconciliation works in React:

  • React can make your reconciliation process.
  • React doesn’t do a full rerender of your DOM nodes. It only changes what it needs to. The diffing process is so fast that you might not notice it.

The post How React Reconciliation Works appeared first on CSS-Tricks.