11 mistakes you're making in React

Our JavaScript man-in-the-know Kevin explores some of the common gotchas that can catch React developers unawares...

01-09-2021
Bcorp Logo


11 mistakes you're making in React


As a freelance developer and teacher, I am in the privileged position of getting to see a lot of React code in the wild. I help support teams as they learn the framework and come to terms with its quirks. I also get to work with codebases that have grown over time. Here are some of the common mistakes that can stop applications being the best they can be.

1. Not enough components

React gives us a lot of freedom in how we structure and organise our applications. It allows us to create a whole application in a single component if we want to. But, as Uncle Ben has told us, with great power comes great responsibility.

A single, large and complex component can be hard to grasp and understand. It can slow down debugging efforts and make extending your application more challenging. Having smaller, single function components allows for cleaner and more readable code. We can combine (or compose) these smaller functions. In this way, we can create complex interfaces out of simpler building blocks. 

This can reduce the amount of copy and pasting in your codebase. Over time, you'll build your own component library, which makes it easier to be consistent with your styling. Additionally, it makes it easier to reason about the implications of any code change.

2. Writing logic in components

React is an amazing front-end framework. With it, we can achieve great things! With backends powered by Node.js or Deno, we can code with JavaScript on both the front and back end. 

One thing I've noticed in teams like this is the temptation not to separate your logic. When you can write your logic anywhere, it can be a challenge to decide where to put it. If we're focused on building smaller, reusable components then this is less of an issue. We stop ourselves adding business logic to our components and focus on the reusable UI. Use helper or utility functions or, even better, have your backend do all the heavy lifting. The React development team work hard to optimise the framework - let's lean in to its strengths!

3. Modifying the state directly

Oh, State! How we love you! How we hate you! For me, state is one of the most interesting topics in development. I love how solutions from decades ago are as relevant today. The next few mistakes are all about State. For those of us arriving to React-land from other part of JavaScript, it can be tempting to change
state in place.

import React from "react";

export default function App() {
  const [person, setPerson] = React.useState({ name: "John" });
  const updateName = () => {
    person.name = "Kevin";
    console.log(person);
  };

  return (
    <div className="App">
      <h1>{person.name}</h1>
      <button onClick={() => updateName()}>Change name</button>
    </div>
  );
}

Here, we're using React's useState hook to initialise a state variable as an object. The only property we've set is the name but this could be a more complex object. We add an onClick handler that calls the updateName function. This updates the name object and then logs the new value. In the
console, everything seems to go fine but the component doesn't rerender. Our underlying data has changed but the UI isn't kept in sync. Instead, we need to use the setter function we get from the useState array.

import React from "react";

export default function App() {
  const [person, setPerson] = React.useState({ name: "John" });

  const updateName = () => {
    const newPerson = {...person};
    newPerson.name = "Kevin";
    setPerson(newPerson);
    console.log(person);
  };

  return (
    <div className="App">
    <h1>{person.name}</h1>
    <button onClick={() => updateName()}>Change name</button>
    </div>
  );
}

That fixes our UI but it raises another mistake that is often made in React development. In development though, a new error is a sign of progress in my book!

4. Forgetting that setState is async

Running the code above and clicking the button causes a problem. If you haven't spotted it head over here, click the button and check the console. Did you spot it? Even as the UI updates to show the new name of Kevin, the logged
value to the console has a name property of John.

This happens because setState is asynchronous. We can't be sure what the value of state will be directly after we set it. React can batch the state calls which can lead to nasty bugs in this
instance. A better approach, would be to deploy a useEffect to handle the logging. 

React.useEffect(() => {
  console.log(person);
}, [person]);

My favourite React hook is useEffect! I love how it simplifies so much legacy code by stripping away the need for lifecycle methods. In this instance, our function logs on the first render and then every time person updates. Perfect!

5. Using useState when no re-render is required

React's useState hook is great. The more we use it, the more we reach for it. It's good to remember that no matter how good our hammer is, not everything is a nail. If you are creating or updating a variable which isn't going to trigger a re-render then use the JavaScript language. It's good to remember that the purpose of this hook is to update the UI based on the data. If the UI isn't dependent on this data, then you can and should use variables outside of React.

6. Not using multiple useEffects

11 mistakes you're making in React


To be able to log our value in a useful way above, we made use of a useEffect hook. The dependency array at the end of the useEffect tells React when it should run the code inside. If it is empty, it will only run on mount making it resemble the old componentDidMount method. We aren't limited to a single instance of this hook. The useEffect hook lets us split the code based on what it is doing rather than the lifecycle method.

React.useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count])

React.useEffect(() => {
  setIsOnline(status.isOnline)
}, [status])

This separation stops unnecessary calculations. We shouldn't update the online status if the count changes and we shouldn't change the title when the status changes. There isn't much of a saving in this example but for more complex operations this is an effective optimisation.

7. Using onClick to trigger navigation

It is possible to put an onClick handler on a button and use that to push to the Router history.
This is how it would look using React Router:

function NavButton({route, label}) {
  const history = useHistory()
  
  const onClick = () => {
    history.push(route);
  };

  return <button onClick={onClick}>{label}</button>
}

This might feel fine but there are problems. The first is an accessibility issue. The onward navigation is hidden inside the button's functionality and so a screen reader is going to find it impossible to deal find. Secondly, hovering over this button won't show the link in the bottom of
left of the browser. This missing hint can be confusing for users. Instead, you should use a Link component to trigger navigation.

function NavButton({route, label}) {
  return <Link to={route}>{label}</Link>
} 


8. Not using KEY on a listing component

11 mistakes you're making in React


Rendering lists of data is one of the most common uses of React in the wild. Whether that is components based on track listing, member lists or car types, the pattern is generally the same.

cars.map((car) => <CarComponent data={car} />)

If you've written code like this before, you'll have seen the following warning in your console:

Each child in a list should have a unique "key" prop.

This key needs to be unique amount the components siblings. The key helps React identify which items have changed, are added or are removed. In some use cases, if you know the data isn't going to change during the render, it might be enough to use the index of the array.

cars.map((car, index) => <CarComponent data={car} key={index} />)<carcomponent data="{car}" key="{index}"></carcomponent>

It is better to use a distinctive data point that identifies the element uniquely, like the id.

cars.map((car) => <CarComponent data={car} key={car.id} />)


9. Forgetting to start a component name with a capital letter

11 mistakes you're making in React


I put this one in because I've helped a few people debug issues along these lines. In order for React to recognise a JSX component, a function component must begin with a capital letter.

function carComponent() {
  return ...
}

This is to separate React components from native HTML elements and components.

function CarComponent() {
...
} function Table() {
return <table> ... </table>
} 

We can implement our own Table component using the native table functionality under-thehood. This PascalCasing is how React allows us to maintain this difference.

10. Not writing Unit Tests

There is a developed testing eco-system that exists within React. My tool of choice is the React Testing Library which is build on top of the DOM Testing Library. This works with projects created with Create React App out of the box. This library lets us write and maintain tests for your components. These automated tests give you confidence that your code works for your users as you intend. The library provides a range of light utilities that help encourage testing best practices. This includes, as much as possible, excluding implementation details from your tests. 

There is a maxim in this community. The closer your tests are to real user interactions, the more you can trust them. Having a test suite allows for faster and more reliable development and deployment.

11. Not using React dev tools

The React team have worked hard to provide developer tools in all the major browsers. When you use these tools, you can see how data is flowing through your application. It is possible to see how React has rendered your application and get an insight into sources of bugs. I know I'm a fan of a good logging statement. These are so useful - to confirm the value of a property, the result of a function or even if a block of code is called. With the dev tools though, we have these abilities but with great enhancements. We can optimise and debug much faster when we have a clearer mental model of what React is doing. The dev tools help us develop and maintain this model.

To summarise...

React is an amazing framework to work with. With this tool, there are so many companies that are doing great things. If you can avoid these mistakes your applications are going to be more
resilient, reliable and effective

Want to learn more about React?

If you found this article interesting and useful, take a look at our React courses:

Here is another of our React related blogs:

Share this post on:

We would love to hear from you

Get in touch

or call us on 020 3137 3920

Get in touch