React 16.6.0 Goodies

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

React.memo() avoids unnecessary re-rendering

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

contextType accesses provider context and passes state without render props

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

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

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

First, let’s create our context:

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

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

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

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

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

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

getDerivedStateFromErrors()

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

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

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

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

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

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

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

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

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

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

Pretty nice, right?

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

More Information

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

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.