React: Error Handling & Conditional Rendering

Contents

Error Handling

Continuing our sample application from the last post we are going to add error handling now. To start we declare another variable in this.state called errorMessage, and then we add that to the error callback on the geolocation function.

Then we add an error into our card in the render section.

class App extends React.Component {
  // javascript class that initializes state
  constructor(props) {
    super(props);

    this.state = { lat: null, long: null, errorMessage:'' };

    window.navigator.geolocation.getCurrentPosition(
      (position) => {
        // we called setstate!
        this.setState({
          lat: position.coords.latitude,
          long: position.coords.longitude,
        });
      },
      (err) => {
        this.setState({ errorMessage: err.message })
      }
    );
  }

  // react requires us to define render
  render() {
    return (
      <div className="ui container">
        <div className="ui card">
          <div className="content">
            <div className="header">Current Position:</div>
            <div className="description">Latitude: {this.state.lat}</div>
            <div className="description">Longitude: {this.state.long}</div>
            <div className="description">Error: {this.state.errorMessage}</div>
          </div>
        </div>
      </div>
    );
  }
}

And we can see that if we deny the location we get the error message on the screen.

user denied geolocation error message

The next step is that we don’t want this error field to be displayed all the time, because even if there isn’t an error we see this.

blank error message

So we want to display this error div conditionally.

Conditional Rendering

We could of course handle this with basic if/else if/ else statements like this:

render() {
    // if there is an error message and no position data
    if (this.state.errorMessage && !this.state.lat) {
      return (
      <div className="ui container">
      <div className="ui card">
        <div className="content">
          <div className="header">Current Position:</div>
          <div className="description">Error: {this.state.errorMessage}</div>
        </div>
      </div>
      </div>
      )
    // if there is no error message and there IS position data 
    } else if (!this.state.errorMessage && this.state.lat){
        return (
        <div className="ui container">
        <div className="ui card">
          <div className="content">
            <div className="header">Current Position:</div>
              <div className="description">Latitude: {this.state.lat}</div>
              <div className="description">Longitude: {this.state.long}</div>
          </div>
        </div>
      </div>
      )
    // if no error message and no position data    
    } else {
        return (
        <div className="ui container">
        <div className="ui card">
          <div className="content">
            <div className="header">Current Position:</div>
            <div className="description">Loading Location...</div>
          </div>
        </div>
      </div>
      )
    }
  }
}

This is not very DRY but it gets the job done.

error message showing conditionally

To DRY this up we can create a simple component for the majority of this, called LatLongCard:

import React from "react";

const LatLongCard = (props) => {
  return (
    <div className="ui container">
      <div className="ui card">
        <div className="content">
          <div className="header">Current Position:</div>
          <div className="description"> {props.children} </div>
        </div>
      </div>
    </div>
  );
};

export default LatLongCard;

And then refactor the app using this component

render() {
    // if there is an error message and no position data
    if (this.state.errorMessage && !this.state.lat) {
      return (
        <LatLongCard>
          <div className="description">Error: {this.state.errorMessage}</div>
        </LatLongCard>
      );
      // if there is no error message and there IS position data
    } else if (!this.state.errorMessage && this.state.lat) {
      return (
        <LatLongCard>
          <div className="description">Latitude: {this.state.lat}</div>
          <div className="description">Longitude: {this.state.long}</div>
        </LatLongCard>
      );
      // if no error message and no position data
    } else {
      return (
        <LatLongCard>
          <div className="header">Current Position:</div>
          <div className="description">Loading Location...</div>
        </LatLongCard>
      );
    }
  }