in

React: Avoid Conditional in Render

For the last several posts we have been working on a little weather application with just a couple of nested components.

class App extends React.Component {
  // initialize state
  state = { lat: null, long: null, errorMessage: "" };

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

  // react requires us to define render
  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>
          <SeasonDisplay lat={this.state.lat} />
        </LatLongCard>
      );
      // if no error message and no position data
    } else {
      return <Loading message='fetching location data' />;
    }
  }
}

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

However so far we have been breaking one of the cardinal rules of React, which is to be placing conditional statements inside of the render function. This is bad for several reasons.

Say for example that no matter which of the three options in the conditional gets chosen, we would like to have a red border always visible around the screen (or a set of navbars…). We would have to create a div for that and repeat it on each of the three conditionals. Repeating code is the enemy.

What we should do instead is break the conditional statement out into a helper method and then reference it inside of the render function. Here we have broken the conditional statement out into a helper function called renderWeatherWidget which cleans up our render function nicely.

class App extends React.Component {
  // initialize state
  state = { lat: null, long: null, errorMessage: "" };

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

  renderWeatherWidget() {
    // 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>
          <SeasonDisplay lat={this.state.lat} />
        </LatLongCard>
      );
      // if no error message and no position data
    } else {
      return <Loading message="fetching location data" />;
    }
  }

  render() {
    return (
      <div className='red-border'>
        {this.renderWeatherWidget()}
      </div>
    );
    
  }
}

You can imagine that if we had something like a dashboard with several widgets and a navbar the render method would quickly become totally unmanageable if we didn’t break conditionals out into helper methods like this.

React Logo

React: Default Props

React Logo

React: Application Structure