Skip to content Skip to sidebar Skip to footer

React Conditionally Rendering

I have a simple form build from React. Upon submission, if the error comes back, I wish to render an extra div to show error on the form. Right now I got it to work but I do not l

Solution 1:

You can just use map in order to extract the error message from your object.

Here below a minimal example of form validations and errors in React. It's good to understand how it works, but for my part, I use Formik which simplifies this process.

class Test extends React.Component {
    constructor(props) {
        super(props);
        this.state = { errorMessages: {} };
    }

    handleRegisterFormSubmit = e => {
        e.preventDefault(); // don't submit the form until we run what's below
        let errorMessages = {};

        if (!this.state.lastName) errorMessages.lastName = 'You need to enter your last name';
        // And so on for each field validation

        // Do we have errors ?
        if (Object.keys(errorMessages).length > 0) {
            this.setState(errorMessages);
        } else {
            // Submit to server
        }
    };

    handleChange = e => {
        this.setState({
            [e.target.name]: e.target.value,
            errorMessages: {
                ...this.state.errorMessages,
                [e.target.name]: null // remove the error of this field if it's being edited
            }
        });
    };

    render() {
        const errArr = Object.keys(this.state.errorMessages).map(key => this.state.errorMessages[key]);
        return (
            <form onSubmit={this.handleRegisterFormSubmit}>
                <div className="form-group">
                    <label>Last Name</label>
                    <input type="text" className="form-control" name="lastName" placeholder="Last Name" value={this.state.lastName} onChange={this.handleChange} />
                </div>

                {/* ... your dom */}

                <div>
                    <button type="submit" className="btn btn-primary">
                        Submit
                    </button>
                    <button type="button" className="btn btn-danger" onClick={this.handleCancelClick}>
                        Cancel
                    </button>
                </div>

                {errArr.length > 0 && (
                    <div className="alert alert-danger">
                      {errArr.map(err => {
                        return <p>{err}</p>;
                      })}
                    </div>
                )}
            </form>
        );
    }
}

One more way to not display your alert div is with a ternary operator for your className and using bootstrap's d-none

<div className={errArr.length ? "alert alert-danger" : "d-none"}>
    {errArr.map(err => {
        return <p>{err}</p>;
    })}
</div>

Solution 2:

I believe this is an architectural question.

Try to follow those practices:

1- Inject conditional statement inside JSX directly

2- Use functional components to render JSX not object methods


1- Inject conditional statement inside JSX directly

Bad:

if (!this.state.errorMessages) {
    return (
        <div>
            {this.renderForm()}
        </div>
    )
} else {
    return (
        <div>
            {this.renderForm()}
            {this.renderError()}
        </div>
    )
}

Good:

return <div>
            {this.renderForm()}
            {this.state.errorMessages && this.renderError()}
        </div>

2- Use functional components to render JSX not object methods

Bad:

class FormComponent {
  // ....

  renderError() {
    // Blah blah blah 
    return (
        <div className="alert alert-danger">
            {errArr.map((err) => {
                return <p>{err}</p>
            })}
        </div>
    )
   }

   render() {
     return (
       <div>
         <AnotherComponent />
         {this.renderError()}
      <div/>
     )
   }

}

then {this.renderError()}

Good

class FormComponent {
  // ....

   render() {
     return (
       <div>
         <AnotherComponent />
         {<Error />} {/* render it as componet ⚠️*/} 
      <div/>
     )
   }

}
// Build it as component outside the main component (FormComponent) ⚠️
function Error(props) { 
    return (
        <div className="alert alert-danger">
            {props.errArr.map((err) => {
                return <p>{err}</p>
            })}
        </div>
     )

 }

I spent many 2 years in React development of enterprise apps also graduating from Udacity and I am sharing my experience here. Congrats!


Solution 3:

One would usually render items in an element representing the list with some styling applied. We also sometimes don't want this wrapping element if we don't have any items.

I've written the following component which allows conditional rendering. When the condition is false, no elements are rendered keeping the DOM clean.

export default class If extends Component<{ condition: any }, {}> {

    render() {
        return this.props.condition ? this.props.children : <Fragment></Fragment>;
    }

}

One can now simply use the component as follow:

<If condition={items.length}>
    <-- item list -->
</If>

Post a Comment for "React Conditionally Rendering"