React and HTML : Beware of the traps

If you ever used React you may have noticed that you can easily forget how to write a static webpage because it adds a layer of abstraction that changes the way the page is created. But in the end, all code written in JSX will generate a classic DOM. In this article I’ll show you mistakes I made and why it is important to write good HTML in Single Page Apps.

 

The unresponsive file input

Context : Creating a button to upload files on a website.

How we did it:

We used a <input type="file"> HTML input. Then we added an eventListener  onChange  which called a handleChange function  that adds the uploaded file as a base64 in component’s state. Then, to delete it, we binded a function removeUploadedFile on click of a button and the file was removed from the list of uploaded file in the state. This is the natural “React way” to create the feature.


class UploadButton extends Component {
  handleButtonCLick = () => {
    this.refs.fileUploader.click();
  }
 
  handleChange = (event) => {
    this.props.handleFileUpload(event);
  }
 
  render() {
    return (
 
      <div style={styles.container} onClick={this.handleButtonCLick} >
        <input type="file" onChange={this.handleChange} style={styles.input} accept={acceptedFileTypesForUpload.join(',')} />
        <AddIcon color={COLORS.BLUE} style={styles.icon} />
      </div>
 
    );
  }
}

What went wrong :

We noticed that sometimes, uploading a document had no effect on our component’s state. We had to spend some time to reproduce the bug : It happened when we added a document, then immediately removed it and added it again. During the last step, the document was not added to the state.

What happened :

The chosen implementation focuses only on the React mechanisms and does not take into account the underlying HTML. Indeed, any HTML input file has a value property associated with it that contains the information of the last document added in the input. But our implementation was only concerned with updating the React state of our component without taking care of this property.

So, when a document was removed, it was taken off the list in the state but not from the “value” property of the input. If we tried to add it again, the value parameter of the input did not change since the doc was already there and the onChange was not triggered.


class UploadButton extends Component { 
  handleChange = (event) => {
    this.props.handleFileUpload(event);
    this.refs.fileUploader.value = ''; // this fixed the bug

 

Everyone hits Enter to submit a form

Context : An authentication form that is not submitted by pressing “Enter” key.

How we did it:

We used two TextField from the library MaterialUI to request username and password and a button to validate. The button had an onClick property that calls a function which triggers an API call to connect the user.


render() {
    return (
        <div>
          <TextField
              id="username"
          />
          <TextField
              id="password"
              type="password"
          />
          <button onClick={this.submitForm}>
              Submit
          </button>
        </div>
    );
}

What happened :

Here too,  using functions of React component called through event listener bypassed the usual way in which a form must be built in HTML ie with <form> tags and a <input type=submit> in the end. This was the right way to create our login form :


render() {
    return (
        <form onSubmit={this.submitForm}>
            <TextField
                id="text-field-default"
                defaultValue="Default Value"
            />
            <input type="submit" value="Submit" />
        </form>
    );
}

Thus usual features (such as validation with input) are missing. It is possible to reinvent them for example with a KeyEventListener here, but what is the point then ?

 

Keep the web semantic!

Context : Reproduce a button by adding an onClick property on a div for example

The problem that arises in this case is that the semantic web is not respected. The semantic web is a set of standards that allow search engines to transform textual information (HTML pages) into machine-readable data.

In this context a <div> tag will be interpreted as an element that contains information and not as an interface element. Thus, using a div tag as a button distorts search engine interpretation and therefore SEO.

Here are some examples of what you should and shouldn’t do:


❌ <div onClick="function">Button</div>
✅ <button onClick="function">Button</button>

❌ <div onClick="function">Link</div>
✅ <a href="link">Link</a>

❌ <div style="list">
    <div style="list-elem">1</div>
    <div style="list-elem">2</div>
   </div>
✅ <ul style="list">
    <li style="list-elem">1</li>
    <li style="list-elem">2</li>
   </ul>

An other frequent mistake comes from the React obligation to enclose components in a unique HTML tag. <div> tags are often added to the DOM structure for no other reason. To prevent that, React introduced Fragments in version v16.2.0. These allow to group many childs in a component without adding an extra tag :


render() {
  return (
    <React.Fragment>
      <li />
      <li />
      <li />
    </React.Fragment>
  );
}

It is even possible to write shorter Fragments tags like this (not supported by all tools)


render() {
  return (
    <>
      <li />
      <li />
      <li />
    </>
  );
}

 

Conclusion

As you can see there are many reason for writing good HTML and it is also very important for one (often forgotten) reason : accessibility.

Accessibility refers to the possibility for people with disabilities to read, understand, navigate and interact with your website. And many features around accessibility (tab navigation, ARIA) are actually based on HTML features. If all your React components are enclosed in useless HTML tags, you’ll turn tab navigation into hell for your disabled users.

A good start the tackle these issues is to install linters like eslint which provide plugins for accessibility: eslint-plugin-jsx-a11y. React is a very powerful tool, but don’t forget the basics!


You liked this article? You'd probably be a good match for our ever-growing tech team at Theodo.

Join Us

  • Florent Destremau

    Didn’t know about the React.Fragment, very nice indeed !

  • Stephan Meijer

    Actually, an `onClick` shouldn’t be on the `a` due to accessibility issues. Correct is `a href…` and `button onClick…`.

  • Elie Dutheil

    you’re right, i’ll update the article thanks for that!

  • Donovan

    Good article! I just now learned about the fragments. Definitely very useful to avoid creating unnecessary wrapper elements.

    In regards to the “unresponsive file input” issue, why not make it a controlled input? Also, what exactly is that “fileUploader” ref? A button? Why not pass the actual function used by the button as a prop?

    In regards to the form issue, something that also went wrong is that there was no wrapper element (or React.Fragment) around the “TextField” components and the button.

  • Elie Dutheil

    Thanks for your feedback !

    For the file input, indeed, a controlled input may have work, it was another mistake. But I thought this example was good to show that people tend to forget what’s behind JSX.

    For the second example, there was actually a wrapper, I just forgot to copy it when I created the snippet, I’ll add it

  • Josh Moncrieff

    Also worth noting that you’ll wanna add the `type=”button”` attribute to the button, lest you want to forever e.preventDefault (as the default behaviour is to submit the parent form) in your click handlers