DEV Community

loading...

Triggering an Error Modal in React

kevhines profile image kevhines ・4 min read

I decided to implement a popup modal which would display error messages in my React application.

I adapted some CSS from Kumar Mathalier's jsfiddle page.
And I adapted some code for how to create a modal from Eden Ella's code on his blog.

I really dug the look of Kumar's modal, and Eden helped direct me to the idea of hiding or showing a component based on a local state. So check those pages for details on how they did those things.

However, Eden's page triggered the modal by clicking a button and I needed my modal to pop up whenever I received an error in my fetch statements. So here's what I did to make this modal appear on it's own.

It all starts when you get an error

When an error occurs in my Rails backend the code sends an error object {message: "some error message"}. So in my fetch statements I need to check to see if I get that object with an error message. When that happens I call an error reducer (instead of whatever reducer I'd call if there was no errors)

 fetch(url, {
        method: "POST",
        headers: {Accept: "application/json", "Content-Type": "application/json"},
        body: JSON.stringify({sentence: body})
      }).then(r => r.json())
      .then(sentence => {
        if (sentence.message) {
          dispatch({type: "DISPLAY_ERROR", payload: sentence})
        } else {
          dispatch({type: "ADD_SENTENCE", payload: sentence})
        }
      })
Enter fullscreen mode Exit fullscreen mode

Redux Store and The Reducer

My reducer does two things. It either sets an error message into my state or it clears it. My state, for purposes of this blog, looks like this:

state = {
other_aspects: {},
error: "" // error message
}

Enter fullscreen mode Exit fullscreen mode

The Modal

Alt Text

The Hulk's trick is that he's always angry and the Error Modal's trick is that it's always there. But it only displays when the props.show changes to true. (Hopefully you looked at the code I linked above that Eden wrote)

I opted to store that Boolean in a local state instead of in props. So this:

class ErrorModal extends React.Component {

    state = {
      show: false
    };
}

Enter fullscreen mode Exit fullscreen mode

In Eden's code he change the value of show with a button click. I want to change that value whenever an error is found (which can happen in any of my fetch calls).

As I showed when my fetch finds an error it sets error in the Redux Store's state. So I made sure to map that error message to props in my Modal Component:

  function mapStateToProps(state) {
    return {error: state.error}
  }

  export default connect(mapStateToProps)(ErrorModal)
Enter fullscreen mode Exit fullscreen mode

Now whenever an error shows up this component will render again. When that happens I cam change the value of my local state. So I used componentDidUpdate()

      componentDidUpdate(prevProps) {
        if (this.props.error && !prevProps.error) {
          this.setState({
            show: true
          });
        }
Enter fullscreen mode Exit fullscreen mode

This if statement is very important. This says if there is an error message in my props AND there wasn't one before set state.show to true. If I didn't have that if statement I could get caught in a recursive loop. Let's say instead of true, I wrote it like this:

          this.setState({
            show: !this.state.show
          });
Enter fullscreen mode Exit fullscreen mode

This has the same result as my code. Before it was hard coded, and this is a little more elegant. It toggles it from false to true. But now think about what happens. The error message changes props which would trigger a render, and that would change the state which would trigger ANOTHER render which would change state again which would trigger ANOTHER render which would trigger ANOTHER render... and you get it.

So that if statement protects me no matter how I change that setState command.

Clear the error message

Ok. The modal pops up. The user is shamed about his mistake. Excellent. They click the button to close the modal. Now we need to hide this modal again. This works almost exactly like Eden's code. His onClick function was a prop and mine is local function, but the same thought process is here:

    onClick = (e) => {
        this.setState({
          show: false
        });
      };

Enter fullscreen mode Exit fullscreen mode

The Modal is no longer visible again (it's back to being mild mannered Bruce Banner if you follow my earlier subtle analogy)

I also needed to remove the error message from the Redux store's state. Changing the state causes the modal to render again but it doesn't make the modal reappear because of that if statement in componentDidUpdate (the prevProps.error is not blank). But when I get a new error prevProps.error will still NOT be blank so the modal won't appear! So I created an action that called the case in my reducer that cleared the message in my store. I called it clearError.

Now I just need to call that after setting state.show to false again.

Here is the final code:

import React from "react";
import {connect} from 'react-redux'
import clearError from '../actions/clearError'

class ErrorModal extends React.Component {

    state = {
      show: false
    };


    onClick = (e) => {
        this.setState({
          show: false
        });
        this.props.clearError()
      };

      componentDidUpdate(prevProps) {
        if (this.props.error && !prevProps.error) {
          this.setState({
            show: true
          });
        }

    }

    render() {
        if(!this.state.show){
            return null;
        }
      return <div className="modal" id="modal">
                <div>
                    <a href="#close" title="Close" className="close" onClick={this.onClick}>X</a>
                    <h2>Error Message</h2>
                    <p>{this.props.error}</p>
                </div>
            </div>
    }
  }

  function mapStateToProps(state) {
    return {error: state.error}
  }

  export default connect(mapStateToProps, {clearError})(ErrorModal)
Enter fullscreen mode Exit fullscreen mode

Conclusion

Props and State changes cause your page to render so managing those values can make it very easy to know when a page will hit their lifecycle methods like componentDidUpdate.

Discussion (0)

Forem Open with the Forem app