» » Common mistakes of juniors using React

Common mistakes of juniors using React

This article highlights some of the mistakes React developers make early in their careers.

Common mistakes of juniors using React

Direct DOM manipulation


This kind of error is especially common among developers who have just migrated from jQuery.

Have you written such code?

import React from "react";

export class Menu extends React.Component {
  componentDidMount() {
    if (window.innerWidth < 500) {
      const menu = document.querySelector(".menu");
      menu.classList.add("mobile");
    }
  }
  render() {
    return <div className="menu">{this.props.children}</div>;
  }
}


What is the problem?


In React, we must avoid direct interactions with the DOM. Instead of getting a reference to the DOM node and adding a class to it directly, you should change the state in the component itself, based on which the class will be added to the element.

What is so bad about direct DOM manipulation?


Any web application is actually built on the state and its management. There is a direct relationship between software complexity and the state that describes it. If your application combines DOM and React state, then the complexity of maintaining it will grow very quickly.

Possible Solution

import React from "react";
export class Test extends React.Component {
  state = {
    isMobile: false
  };
  componentDidMount() {
    if (window.innerWidth < 500) {
      this.setState({
        isMobile: true
      });
    }
  }
  render() {
    return (
      <div className={this.state.isMobile ? "mobile" : ""}>
        {this.props.children}
      </div>
    );
  }
}


Let's take a look at how we're using React's state to update an attribute classNameon our component, and as a result, we got rid of the document.querySelector. Fine!

Don't follow resources


Newcomers to development often write code like this when they work with events. Take, for example, a simple React component that does… something when the user presses spacebar.

import React from "react";

export class CaptureSpace extends React.Component {
  componentDidMount() {
    document.body.addEventListener("keydown", event => {
        if (event.keyCode === 32) {
          // do something when user hits spacebar
        }
    });
  }
render() {
    return (
       //
    );
  }
}


Notice how we added an event listener but didn't take care to remove it at the end?

This can lead to memory leaks and subtle issues in the future. The best solution is to remove subscribers before our component is removed from the DOM.

Let's take a look at the solution below:

import React from "react";

export class CaptureSpace extends React.Component {
  componentDidMount() {
    document.body.addEventListener("keydown", this.captureSpace);
  }
componentWillUnmount() {
    document.body.removeEventListener("keydown", this.captureSpace);
  }
captureSpace = event => {
    if (event.keyCode === 32) {
      // do something when user hits spacebar
    }
  };
render() {
    return (
       //
    );
  }
}


Refusal of tests (or insufficient number of them)


If I was given a dollar for every project I reviewed where the only test was the default one in create-react-app, I wouldn't be writing this article. And, probably, he was sipping a daiquiri somewhere on the beach now.

What is it about testing that scares the juniors so much? I think the reason lies in the non-triviality of testing components, especially given their constant development and growth in complexity. I often see junior programmers write unit tests for certain pure functions, but fail when they need to write an integration test for an entire component.

Maybe the stubs are confusing them? Or do they struggle with what should be tested and what should not?

Let's take a look at the component I just wrote. This is a very simple login form where the user must enter their username and password. When the latter confirms the entered information, we make an API call and, if the answer is yes, we direct the client to another page.

import React from "react";
import axios from "axios";

export const LoginForm = props => {
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");
return (
    <form
      onSubmit={async event => {
        event.preventDefault();
        const result = await      axios.post("https://reqres.in/api/login", {
          email,
          password
        });
       if (result.data.token) {
          window.localStorage.setItem("token", result.data.token);
          window.location.replace("/home");
        }
      }}
    >
      <div>
        <label htmlFor="email">Email</label>
        <input
          id="email"
          onChange={event => setEmail(event.target.value)}
          value={email}
        />
      </div>
      <div>
        <label htmlFor="password">Password</label>
        <input
          id="password"
          type="password"
          onChange={event => setPassword(event.target.value)}
          value={password}
        />
      </div>
      <button type="submit">Submit</button>
    </form>
  );
};


So how do you test this form?


First of all, let's take a look at how our user will interact with it.

The user enters their data.
The user clicks on the confirmation button.
The user is redirected to the "home" page.
This is all we need to test.

Below I wrote one of the test cases for this component. Can you suggest any other scenarios that would be useful to test?

import React from "react";
import { LoginForm } from "./Login";
import axios from "axios";
import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

jest.mock("axios");
describe("LoginForm", () => {
  describe("when a user types in a correct email and password", () => {
    it("should redirect to home page", async () => {
      const rendered = render(<LoginForm />);
      const emailInput = rendered.getByLabelText("Email");
      await userEvent.type(emailInput, "[email protected]");
      const passwordInput = rendered.getByLabelText("Password");
      await userEvent.type(passwordInput, "1234");
      delete window.location;
      window.location = { replace: jest.fn() };
      const data = { data: { token: "fake-token" } };
      axios.post.mockImplementationOnce(() => Promise.resolve(data));
      userEvent.click(rendered.getByText("Submit"));
      expect(window.location.replace).toHaveBeenCalledWith("/home");
    });
  });
});


Misunderstanding Webpack


Some of the junior developers I worked with knew how to use but didn't understand how Webpack worked. They only used it with the main codebase of the project and believed that everything else “works just because”. They didn't dig deeper, figure out exactly how the CSS and ES6 they write is transformed and combined into what is ultimately used by the client browser.

I recommend that every React developer take the time to build a simple template project. Instead of relying on create-react-app and NextJS every time, understand how modern jаvascript build tools work together. This will improve your understanding of your work and, as a result, make you a more efficient developer, especially when solving build problems.

Related Articles

Add Your Comment

reload, if the code cannot be seen

All comments will be moderated before being published.