Mistakes Junior React Developers Make

And how to avoid them

Malcolm Laing
Feb 23 · 5 min read
Photo by Francesco Gallarotti on Unsplash

Over the course of my career, I have worked with and mentored many junior developers. I have seen their strengths and weaknesses, and I have seen them grow and become more complete developers.

A few days ago I was reviewing some code written by a new React developer. What follows are some of the mistakes that they made, and some traps that I have seen less-experienced developers fall into over the years.

This post isn’t meant to judge or mock. We all start out as junior developers, and I have made many mistakes as well. Like the time I sent out a test email to 500 real clients. But I digress…


This kind of thing is very common with new developers, especially developers who have worked with jQuery before.

Have you ever written code like this before?

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’s the problem?

In React, we should avoid direct dom manipulation. Instead of accessing the dom element and adding a class to it, we should our state inside of our React component, and use that state to add the class to our component.

What’s so wrong about direct dom manipulation?

Creating web applications is all about managing application state. The bigger your application grows, the larger and more complex your state becomes. If your application mixes up state inside of the DOM, and inside of React, it will get harder to reason out, harder to test, and harder to debug.

A 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>
);
}
}

Notice how we are now using our React state to update the className of our component, and we have removed usage of document.querySelector. Nice!


New developers often write this kind of code when they start working with events. Let’s take for example a simple React component that does … something when the user hits the 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 we didn’t remove it afterwards? This can lead to memory leaks, and hard to debug problems. It’s best practice to remove our event listeners before our components unmount.

Check out 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 (
//
);
}
}

If I had a dollar for every coding challenge I have reviewed where the only test was the default test case from create-react-app, I wouldn’t be writing this article. I would probably be sipping daiquiris on a beach somewhere.

What is it about testing that makes junior developers so scared? I think that the more complex a component or application becomes, the more daunting the task of writing tests for it becomes. I often see junior developers write unit tests for specific pure functions, but fail to write integration tests for whole components.

Maybe mocking trips them up? Or maybe they have trouble figuring out what to test?

Let’s take a look at a component I just wrote. You have probably written something like it a few times. It’s a simple login form, where the user types in their username and password. When the form submits, we do an API call, and if the response is positive, we redirect the user to a different 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 test this login form?

First, let’s take a look at how our users will interact with this component. Let’s jot down the user flow.

  1. The user types in their username and password
  2. The user clicks the submit button
  3. The user is redirected to the “home” page.

This is what we need to test.

Below I have written one test case for this component. Can you think of some others that would be useful?

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, "john@gmail.com");
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");
});
});
});

Few junior developers that I have worked with understand how to use Webpack. They work with the codebase that they are provided and just assume everything works. They don’t dive in and figure out how the CSS and ES6 they write are transformed and bundled to end up in the user's browser.

I recommend that all React developers take the time to set up their own boilerplate project. Instead of always relying on create-react-app and NextJS, take the time to figure out how modern JavaScript build tools work together. It will give you a better understanding of your work, and it will make you better at debugging build problems.


Can you think of other mistakes you made when you were a junior, or mistakes that you have seen juniors make? I would love to hear about them in the comments!

Frontend Digest

Anything and everything frontend. JavaScript, CSS and HTML.

Malcolm Laing

Written by

JavaScript Consultant. Senior React developer. Still makes silly mistakes daily.

Frontend Digest

Anything and everything frontend. JavaScript, CSS and HTML.

More From Medium

More from Frontend Digest

More from Frontend Digest

More from Frontend Digest

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade