DEV Community

Cover image for 7 Ways to Write Cleaner React Code Today
Reed Barger
Reed Barger

Posted on • Originally published at reedbarger.com

7 Ways to Write Cleaner React Code Today

📣 This post originally appeared on ReedBarger.com.

As React developers, we all want to write cleaner code that is simpler and easier to read.

In this guide, I've put together seven of the top ways that you can start writing cleaner React code today to make building React projects and reviewing your code much easier.

In general, learning how to write cleaner React code will make you a more valuable and overall happier React developer, so let's jump right in!

Want the complete guide to writing clean React code from start to finish? Check out The React Bootcamp.

1. Make use of JSX shorthands

How do you pass a value of true to a given prop?

In the example below, we're using the prop showTitle to display the title of our app within a Navbar component.

// src/App.js

export default function App() {
  return (
    <main>
      <Navbar showTitle={true} />
    </main>
  );
}

function Navbar({ showTitle }) {
  return (
    <div>
      {showTitle && <h1>My Special App</h1>}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Do we need to explicitly set show title to the Boolean true? We don't! A quick shorthand to remember is that any prop provided on a component has a default value of true.

So if we add the prop showTitle on Navbar, our title element will be shown:

// src/App.js

export default function App() {
  return (
    <main>
      <Navbar showTitle />
    </main>
  );
}

function Navbar({ showTitle }) {
  return (
    <div>
      {showTitle && <h1>My Special App</h1>} // title shown!
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Another useful shorthand to remember involves passing string props. When you're passing a prop value that's a string, you don't need to wrap it in curly braces.

If we are setting the title of our Navbar, with the title prop, we can just include its value in double quotes:

// src/App.js

export default function App() {
  return (
    <main>
      <Navbar title="My Special App" />
    </main>
  );
}

function Navbar({ title }) {
  return (
    <div>
      <h1>{title}</h1>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

2. Move unrelated code into a separate component

Arguably the easiest and most important way to write cleaner React code is to get good at abstracting our code into separate React component.

Let's look at the example below. What is our code doing?

Our app is displaying a Navbar component. We are iterating over an array of posts with .map() and displaying their title on the page.

// src/App.js

export default function App() {
  const posts = [
    {
      id: 1,
      title: "How to Build YouTube with React"
    },
    {
      id: 2,
      title: "How to Write Your First React Hook"
    }
  ];

  return (
    <main>
      <Navbar title="My Special App" />
      <ul>
        {posts.map(post => (
          <li key={post.id}>
            {post.title}
          </li>
        ))}
      </ul>
    </main>
  );
}

function Navbar({ title }) {
  return (
    <div>
      <h1>{title}</h1>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

How can we make this cleaner?

Why don't we abstract the code that we're looping over--our posts--and display them in a separate component, which we'll call FeaturedPosts.

Let's do that and take a look at the result:

// src/App.js

export default function App() {
 return (
    <main>
      <Navbar title="My Special App" />
      <FeaturedPosts />
    </main>
  );
}

function Navbar({ title }) {
  return (
    <div>
      <h1>{title}</h1>
    </div>
  );
}

function FeaturedPosts() {
  const posts = [
    {
      id: 1,
      title: "How to Build YouTube with React"
    },
    {
      id: 2,
      title: "How to Write Your First React Hook"
    }
  ];

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we can now just look at our App component and by reading the names of the components within it, Navbar and FeaturedPosts, we see exactly what our app is displaying.

3. Create separate files for each component

Going off of our previous example, we are including all of our components within a single file, the app.js file.

Similar to how we abstract code into separate components to make our app more readable, to make our application files more readable, we can put each component that we have into a separate file.

This, again, helps us separate concerns in our application, meaning that each file is responsible for just one component and there's no confusion where a component comes from if we want to reuse it across our app:

// src/App.js
import Navbar from './components/Navbar.js';
import FeaturedPosts from './components/FeaturedPosts.js';

export default function App() {
  return (
    <main>
      <Navbar title="My Special App" />
      <FeaturedPosts />
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode
// src/components/Navbar.js

export default function Navbar({ title }) {
  return (
    <div>
      <h1>{title}</h1>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
// src/components/FeaturedPosts.js

export default function FeaturedPosts() {
  const posts = [
    {
      id: 1,
      title: "How to Build YouTube with React"
    },
    {
      id: 2,
      title: "How to Write Your First React Hook"
    }
  ];

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

Additionally, by including each individual component within its own file, we avoid one file becoming too bloated. We could easily see our app.js file becoming very large if we wanted to add all of our components into that file.

4. Move shared functionality into React hooks

Taking a look at our FeaturedPosts component, let's say instead of displaying static posts data, we want to fetch our post data from an API.

We might do so with the fetch API. You can see the result below for that:

// src/components/FeaturedPosts.js

import React from 'react';

export default function FeaturedPosts() {
  const [posts, setPosts] = React.useState([]);     

  React.useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(res => res.json())
      .then(data => setPosts(data));
  }, []);

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

However, what if we wanted to perform this request for data across multiple components?

Let's say in addition to a FeaturedPosts component we wanted to create a component called just Posts with the same data. We would have to copy the logic that we used to fetch our data and paste it within that component as well.

To avoid having to do that, why don't we just use a new React hook which we could call useFetchPosts:

// src/hooks/useFetchPosts.js

import React from 'react';

export default function useFetchPosts() {
  const [posts, setPosts] = React.useState([]);     

  React.useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/posts')
      .then(res => res.json())
      .then(data => setPosts(data));
  }, []);

  return posts;
}
Enter fullscreen mode Exit fullscreen mode

Once we've created this hook in a dedicated 'hooks' folder we can reuse it in whichever components we like, including our FeaturedPosts component:

// src/components/FeaturedPosts.js

import useFetchPosts from '../hooks/useFetchPosts.js';

export default function FeaturedPosts() {
  const posts = useFetchPosts()

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

5. Remove as much JavaScript from your JSX as possible

Another very helpful, but often neglected way to clean up our components is to remove as much JavaScript from our JSX as possible.

Let's take a look at the example below:

// src/components/FeaturedPosts.js

import useFetchPosts from '../hooks/useFetchPosts.js';

export default function FeaturedPosts() {
  const posts = useFetchPosts()

  return (
    <ul>
      {posts.map((post) => (
        <li onClick={event => {
          console.log(event.target, 'clicked!');
        }} key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

We're trying to handle a click event on one of our posts. You can see that our JSX becomes much harder to read. Given that our function is included as an inline function, it obscures the purpose of this component, as well as the its related functions.

What can we do to fix this? We can extract the inline function, connected to the onClick into a separate handler, which we can give a an appropriate name like handlePostClick.

Once we do, our JSX becomes readable once again:

// src/components/FeaturedPosts.js

import useFetchPosts from '../hooks/useFetchPosts.js';

export default function FeaturedPosts() {
  const posts = useFetchPosts()

  function handlePostClick(event) {
    console.log(event.target, 'clicked!');   
  }

  return (
    <ul>
      {posts.map((post) => (
        <li onClick={handlePostClick} key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

6. Format inline styles for less bloated code

A common pattern for React developers is to write inline styles in their JSX but once again, it makes our code harder to read and harder to write additional JSX:

// src/App.js

export default function App() {
  return (
    <main style={{ textAlign: 'center' }}>
      <Navbar title="My Special App" />
    </main>
  );
}

function Navbar({ title }) {
  return (
    <div style={{ marginTop: '20px' }}>
      <h1 style={{ fontWeight: 'bold' }}>{title}</h1>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

We want to apply this concept of separation of concerns to our JSX styles by moving our inline styles into a CSS stylesheet, which we can import into whatever component we like.

An alternative way to rewrite your inline styles is by organizing them into objects. You can see what such a pattern would look like below:

// src/App.js

export default function App() {
  const styles = {
    main: { textAlign: "center" }
  };

  return (
    <main style={styles.main}>
      <Navbar title="My Special App" />
    </main>
  );
}

function Navbar({ title }) {
  const styles = {
    div: { marginTop: "20px" },
    h1: { fontWeight: "bold" }
  };

  return (
    <div style={styles.div}>
      <h1 style={styles.h1}>{title}</h1>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

7. Reduce prop drilling with React context

Another essential pattern to employ for your React projects, especially if you have common properties that you want to reuse across your components, and you find yourself writing lots of duplicate props, is to use React Context.

For example, if we wanted to share user data across multiple components, instead of multiple repeat props (a pattern called props drilling), we could use the context feature that's built into the React library.

In our case, if we wanted to reuse user data across our Navbar and FeaturedPosts components, all we would need to do is wrap our entire app in a provider component.

Next, we can pass the user data down on the value prop and consume that context in our individual components with the help of the useContext hook:

// src/App.js

import React from "react";

const UserContext = React.createContext();

export default function App() {
  const user = { name: "Reed" };

  return (
    <UserContext.Provider value={user}>
      <main>
        <Navbar title="My Special App" />
        <FeaturedPosts />
      </main>
    </UserContext.Provider>
  );
}

// src/components/Navbar.js

function Navbar({ title }) {
  const user = React.useContext(UserContext);

  return (
    <div>
      <h1>{title}</h1>
      {user && <a href="/logout">Logout</a>}
    </div>
  );
}

// src/components/FeaturedPosts.js

function FeaturedPosts() {
  const posts = useFetchPosts();
  const user = React.useContext(UserContext);

  if (user) return null;

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

I hope you find this guide helpful as a resource to find ways to improve your own React code that is cleaner, easier to read, and ultimately more enjoyable to create your React projects.

Enjoy this post? Join The React Bootcamp

The React Bootcamp takes everything you should know about learning React and bundles it into one comprehensive package, including videos, cheatsheets, plus special bonuses.

Gain the insider information hundreds of developers have already used to master React, find their dream jobs, and take control of their future:

The React Bootcamp

Click here to be notified when it opens

Discussion (8)

Collapse
thomasledoux1 profile image
Thomas Ledoux

Nice article! Maybe it would be good to include the shorthand for an "if/else" check in the first part? For example:

export default function App() {
  return (
    <main>
      <Navbar showTitle />
    </main>
  );
}

function Navbar({ showTitle }) {
  return (
    <div>
      {showTitle ? (<h1>My Special App</h1>) : (<h1>Default title</h1>)}  //default title is shown when showTitle is false
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode
Collapse
reedbarger profile image
Reed Barger Author

I would write your code snippet as follows, without having to write the h1 element twice. Cuts down on our code a bit:

export default function App() {
  return (
    <main>
      <Navbar showTitle />
    </main>
  );
}

function Navbar({ showTitle }) {
  return (
    <div>
      <h1>{showTitle ? "My Special App" : "Default title"}</h1>
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode
Collapse
thomasledoux1 profile image
Thomas Ledoux

Oh yea, in this case that’s certainly possible! My code would be more useful when you have to use another element instead of the

:-)

Collapse
pengeszikra profile image
Peter Vivo • Edited on

Maybe arrow function much more minimalist, special when component don't contain any JS code.

export const FooComponent = ({content, title, handleClick}) => (
  <section className="foo-component" onClick={handleClick}>
     <header>{title}<header>
     <p className="foo-component--content">{content}</p>
  </section>
);
Enter fullscreen mode Exit fullscreen mode
Collapse
velociwabbit profile image
Info Comment hidden by post author - thread only accessible via permalink
velociwabbit

Let me guess... you have less than 5 years coding in the real world and are following what your teachers told you. No doubt they learned java and still think like the group at sun who decided that separating all classes into files was a great idea (java was the first to do this as a requirement. )

There are a set of principles that all very experienced developers eventually realize. It takes java coders longer but after 20 years there is nearly uniform agreement.

0 - Avoid engineering theatre... this comes in all sorts of items ... long variable names, extra {} unnecessary ; abstraction to for abstraction's sake. Using complex or esoteric features when simpler ones will do. Adding extra files just because JAVA!

1 - Touch variables minimally... for example if you use a style only once then place it where you use it. do not instantiate it somewhere else then use it only one time in the code... this is extra code and therefore extra technical risk.

2 Do not create one off functions unless absolutely necessary

3 Location , location, location... when you code keep all the code relative to one function as close as possible that way when you debug you have a minimal amount of bouncing around. Separation theatre because it looks nicer is creating more opportunity for bugs

4 "Goto" has been replaced by over modulation and other theater. The point behind banning goto is that it bounced you around code and made it difficult to trap and debug and therefore maintain. The best code has minimal branching and can be followed as much as possible linearly without opening other files. This is obviously not completely possible (like zero defect) but it is a goal that makes code both fast and elegant.

5 Avoid directory vertigo by flattening your repos... This avoids unforced errors caused by referencing the right file name in the wrong directory

6 Do not rely too heavily on the syntax of any one application... the next release will burn you. If you want proof consider how react has evolved since you started coding

-It started with CreateClass where javascript functions with prototype extensions were used

-Then "Class" replaced createClass and "Function.protype" use was discouraged if not deprecated

-Then oops classes are really slow so let's use fat arrow functions

-Now back to functions as the prefered means for generating components.

During that same timeframe take a look at Three.js code. Mr Doob knows what he is doing and did not get faked out by the inexperience of these new declarative coding "geniuses". While i do not agree with all of his coding layout decisions it is crystal clear and one can jump in and out even after 4 or 5 years.

7 Use common sense... making something look cool because others are doing it that way (such as using that rubiks cube called redux) is a great way to get a promotion out of coding into management. (not a complement)

8 learn c or c++, and attempt to learn assembly. It will teach you how to de ornament your code.

btw i deliberately avoided commenting on your code because now that your code is done... it is done... move on . I realize this goes against the always be refactoring and in general i agree with the massively iterable model. There is one counter argument that also should be considered.

Things that are not worth doing are also not worth doing well

Collapse
braydentw profile image
Brayden W ⚡️

Awesome tips - Thanks!

Collapse
manuthecoder profile image
Manu

Very useful! Thank you. I'm actually learning react now, and thanks for the article!

Collapse
kodikos profile image
Jodi Winters

Hi.. good tips!
Just one thing I note with No. 5, isn't it better to put the event handler in a useCallback hook? Then it's not re-evaluated on every call. Probably trivial in this example, but with larger event handlers, that might improve performance.

Some comments have been hidden by the post's author - find out more