Jalan Technologies > Blog >

Best Practices for Clean React Apps

This blog highlights essential React coding practices for creating maintainable and readable code. Covering topics from destructuring to error boundaries, it offers tips on optimizing JSX, naming conventions, async/await usage, and the importance of memoization techniques. Enhance your React development skills for improved code quality and efficiency.

React is a front-end framework and one of the most popular JavaScript libraries to build user interfaces on the web. With its diverse features, it provides a lot of flexibility to create a wide variety of applications. We may be writing code and building great UIs that are working.

However, writing code just to create apps isn’t enough; the code should be clean and readable to help us in developing maintainable apps. Clean code is not just code that works, but rather code that can be easily read, reused, refactored, extended, and scaled by others. Hence, we should write code in a manner that it should be self-explanatory, easy to read and understand, and helps in ensuring consistency.

This blog aims to present a compiled version of some widely accepted writing styles and practices to keep in mind while working with React that should be helpful to both intermediate and experienced developers.

1. Use Object Destructuring: Object destructuring allows you to take specific fields from an object and assign them to a variable instantly. It reduces the number of code lines we need to extract the object properties and makes your code easier to understand. Object destructuring saves a great deal of explicit variable declarations, and it is really useful in situations when:

  • Using multiple properties from an object.
  • Using the same property multiple times.

Refer to the example below on how to use destructuring for a case where we need to show a user’s details.

<div>{user.name}</div>
<div>{user.age}</div>
<div>{user.profession}</div>
const { name, age, profession } = user;
return (
  <>
    <div>{name}</div>
    <div>{age}</div>
    <div>{profession}</div>
  </>
);

2. Props Destructuring: The above approach of object destructuring can also be leveraged while accessing props.

 const NavBar = (props) =>
const NavBar = ({ showNotifications, showProfile, }) =>

3. Array Destructuring: An often overlooked ES6 feature is array destructuring. It allows us to access array values without specifying indexes.

const splitLocale = locale.split('-');
const language = splitLocale[0];
const country = splitLocale[1];
const [language, country] = locale.split('-');

4. Use Template Literals: Use template literals to build large strings. Avoid using string concatenation. It’s nice and clean.

const userDetails = user.name + "'s profession is" + user.profession;
return <div>{userDetails}</div>;
const userDetails = `${user.name}'s profession is ${user.profession}`;
return <div> {userDetails} </div>;

5. JSX Short Hand: Use JSX shorthand for passing explicit true boolean props to Components. For example, if you want to make your title visible in your NavBar Component:

<Navbar showTitle={true} />
<Navbar showTitle />

6. Use Ternary Operators: For cases where we know that we expect only 2 possible options, use ternary operators. Let’s say you want to show a particular user’s details based on role.

const { role } = user;
return (
  <>
    {role === ADMIN && <AdminUaser />
    {role !== ADMIN && <BasicUser />
  </>
);

The above code causes the equality check to be evaluated 2 times. With ternary, we can lower it to once.

const { role } = user;
return role === ADMIN ? <AdminUser /> : <BasicUser />;

7. Take Advantage of Object Literals: Object literals can help make our code more readable. Let’s say you want to show three types of users based on their roles. Using ternary is not suggested because the number of options is greater than two (it would need nesting). Object literals can be used instead.:

const { role } = user;
switch (role) {
  case ADMIN:
    return <AdminUser />;
  case EMPLOYEE:
    return <EmployeeUser />;
  case USER:
    return <NormalUser />;
}
const { role } = user;
const components = {
  ADMIN: AdminUser,
  EMPLOYEE: EmployeeUser,
  USER: NormalUser,
};
const Component = components[role];
return <Componenent />;

In addition to increasing readability in such scenarios, we can also employ object literals to map dynamic or computed properties.

8. Use Fragments: Always use Fragment over div or span as containers. It keeps the code clean and is also beneficial for performance because we are not adding useless containers to the DOM tree, hence one less node is created in the virtual DOM.

return (
  <div>
    <Component1 />
    <Component2 />
    <Component3 />
  </div>
);
return (
  <>
    <Component1 />
    <Component2 />
    <Component3 />
  </>
);

9. Don’t Define a Function Inside Render: Don’t define a function inside render. Move any JS code out of JSX if that doesn’t serve any purpose of rendering or UI functionality. Try to keep the logic inside render to an absolute minimum.

return (
  <button
    onClick={(event) => {
      console.log(event.target, "clicked!"); // Avoid this
    }}
  >
    Submit
  </button>
);
const submitData = (event) => {
  console.log(event.target, "clicked!");
};
return <button onClick={submitData}>Submit</button>;

10. Reusing Expressions with Variables: Declare variables in the block scope code for reusability. As soon as you see the need to use a condition or expression more than once, extract it as a variable instead of repeating the expressions over and over.

function getUserPreferences(user) {
  if (user.basicDetails.role === 'ADMIN') {
    // ...
  } else if (user.basicDetails.role === 'OTHER') {
    // ...
  }
}
function getUserPreferences(user) {
  const role = user.basicDetails.role;
  if (role === "ADMIN") {
    // ...
  } else if (role === "OTHER") {
    // ...
  }
}

11. String Props Don’t Need Curly Braces when being passed to a child component.

<NavBar title={"My App"} />
<NavBar title="My App" />

12. Lists and the Key prop: The “key” prop is a special string attribute you need to include when rendering lists of elements. React uses keys to uniquely identify items in an array. With keys, React can pinpoint which item has been changed, added, or removed from the array.

Most of the time when rendering arrays, we might tend to use the index as the key. While this sometimes works, using the index as a key can introduce issues especially if the list is expected to change. Consider this list –
const arr = ["item1", "item2", "item3", "item4", "item5"];

{arr.map((elem, index) => <li key={index}>{elem}</li>)}

Currently, the first list item, “Item1” is at index zero, but if you added another item at the beginning of the list, the “Item1” index would change to 1 which changes the behaviour of your array. Hence make sure to use unique values as keys.

{arr.map((elem, index) => <li key={elem}>{elem}</li>)}

13. Arrow functions & implicit return: Use the JavaScript feature of implicit return in arrow functions with to write beautiful code. Let’s say your function does a simple calculation and returns the result.

function add(a, b) {
  return a + b;
}
const add = (a, b) => a + b;

Similarly while using arrow function in JSX-

{users.map((user) => {
  return <div key={user.id}>{user.name}</div>;
})}
{users.map((user) => <div key={user.id}>{user.name}</div>}

14. Import Ordering: Always try to import things in the following recommended order. The rule of thumb is to keep the import order like this:

● Built-in
● External
● Internal

It improves code readability. Most editors like VSCode provide an option to automatically order the imports on file save.

import React from 'react';
import ErrorImg from '../../assets/images/error.png';
import styled from 'styled-components/native';
import colors from '../../styles/colors';
import { PropTypes } from 'prop-types';

So the example above becomes:

import React from 'react';
import { PropTypes } from 'prop-types';
import styled from 'styled-components/native';
import ErrorImg from '../../assets/images/error.png';
import colors from '../../styles/colors';

15. File & Folder Structure: One of the most commonly followed files & folder structure is as follows:-

└── /src
├── /assets – Contains static assets – Icon Svgs, banner images etc.
├── /components – reusable components like navigation bar, buttons, forms
├── /services – JavaSript modules
├── /utils – utilities, helpers, constants.
├── /views/pages – Majority of the app pages would be here
├── index.js
└── App.js

In turn, each component folder should have its respective files grouped under the same. This maintains a consistent hierarchy throughout the codebase. Eg. For the footer component, the necessary files could include- src/components/footer/index.tsxsrc/components/footer/styles.scsssrc/components/footer/stories.tsxsrc/components/footer/footer.test.tsxsrc/components/footer/types.ts, etc.

16. Naming Conventions:

Always use PascalCase for components and camelCase for instances & prop names.

import formHelper from './formHelper';
const form_Helper = new formHelper();

<MyComponent
  UserName="hello"
  phone_number={12345678}
/>
import FormHelper from './FormHelper';
const formHelper = new FormHelper();

<MyComponent
  userName="hello"
  phoneNumber={12345678}
/>

17. Naming Rationales:

Boolean variables, or functions that return a boolean value, should start with “is,” “has” or “should”.

const done = tasks.length === 0;
const isComplete = tasks.length === 0;

Likewise, functions should be named for what they do, not how they do it. In other words, don’t expose details of the implementation in the name. Why? Because how you do it may change someday, and you shouldn’t need to refactor your consuming code because of it. For example, you may load your config from a REST API today, but you may decide to bake it into JavaScript tomorrow.
Also, avoid underscores in function names. (_doSomething())

const getConfigFromServer = () =>
const loadConfig = () =>

18. Reserved Prop Name Usage: Don’t use DOM component prop names for passing props between components because others might not expect these names.

<MyComponent style="dark" />
// OR
<MyComponent className="dark" />
<MyComponent theme="dark" />

19. Usage of Quotes: Use double quotes for JSX attributes and single quotes for all other JS.

<Foo bar='bar' />
<Foo features={{errors: 'none' }} />
<Foo bar="bar" />
<Foo features={{ errors: 'none' }} />

20. JSX in Parentheses: If the JSX that is being returned by your component or iterator spans more than one line, always wrap it in parentheses.

return <MyComponent variant="long">
  <MyChild />
</MyComponent>;
return (
  <MyComponent variant="long">
    <MyChild />
  </MyComponent>
);

21. Self-Closing Tags: If your component doesn’t have any children, then use self-closing tags. It improves readability.

<Header showMenu></Header>
<Header showMenu />

22. Sanitize HTML to prevent XSS Attacks:  Maybe you’ve found yourself in a scenario where you have to use the property dangerouslySetInnerHTML on an element in React, say when having to render the result coming from a rich text editor. Basically, it’s React’s equivalent to Javascript’s innerHTML.

const Markup = () => {
  const htmlString = "<p>This is set via dangerouslySetInnerHTML</p>";
  return <div dangerouslySetInnerHTML={{ __html: htmlString }} />;
};

The term dangerously is chosen with intention. Using this property can open you up to a cross-site scripting (XSS) attack. So it’s highly recommended that the code that gets set is sanitized first. You could either use a custom-written utility to filter out risky code snippets or use external libraries to help with this.

DOMPurify is one good example of this.

23. Using Spread Operator to pass Props: If you have an object, and you want to pass all its keys in JSX, you can use ... as a “spread” syntax to pass the whole object. It helps with avoiding unnecessary object property access & repetition.

<ProductCard name={item.name} price={item.price} />
<ProductCard {...item} />

24. Using Optional Chaining: Increases readability and simplifies otherwise long logical conjunctions and complex-looking conditions.

const showPublishers = books && books.publishers && books.publishers.length > 0;
const showPublishers = books?.publishers?.length > 0;

25. Shorthand Logic Evaluation: Covering below some examples of cases that return truthy/falsy values that can be relatively shortened.

length > 0
name !== "" && name !== undefined && name !== null

If using such conditions in javascript code blocks like function, they can be written as:

if(length) // …
if(name) // …

If using inside JSX, they can be first converted to Boolean using the double negation (!!) operator as:

{!!length &&}
{!!name &&}

26. Img Alt prop: Always include an alt prop in your <img> tags. And, we don’t need to use pictures or images in alt property because the screen readers already announce img elements as images.

<img src="boat.jpg" />
<img src="boat.jpg" alt="Picture of a person rowing a boat" />
<img src="hello.jpg" alt="Me waving hello" />

27. Default Props: It is considered a good practice to set a default value for otherwise optional props.

const NavBar = ({ showNotifications }) => ...
const NavBar = ({ showNotifications = false }) => ...
// OR
const NavBar = ({ showNotifications }) => {};
NavBar.defaultProps = {
  showNotifications: false,
};

28. Inline styles: It is not considered good practice to use inline styles in React components. Always make use of options like stylesheets, CSS modules, styled-components, etc.

<div style={{ display: 'none' }}>text</div>
<div className="hide">text</div>

29. Async/await over Callbacks: Promises & callbacks have been the pillar pattern for working with asynchronous code. However, the introduction of async-await has helped achieve the same functionality, in addition to providing better readability and overcoming certain limitations like issues with nested callbacks (callback hells), etc.

fetch("https://jsonplaceholder.typicode.com/users")
  .then((response) => response.json())
  .then((response) => console.log(response))
  .catch((err) => console.error(err));
async function getUsers() {
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/users");
    const users = await response.json();
    console.log(users);
  } catch (error) {
    console.error(error);
  }
}

30. Use Error Boundaries: As much as we try to make our application robust, errors are still bound to occur. We create user-friendly UIs to handle such error cases. To guard our app against crashing entirely, React provides the feature of Error boundaries through which we can display a fallback UI should an unexpected error occur. You can make use of React’s ErrorBoundary implementation as shown below or use a package like react-error-boundary.

const App = () => {
  return <Component>...</Component>;
};
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    logError(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Oops, something went wrong.</h1>;
    }
    return this.props.children;
  }
}

const App = () => {
  return (
    <ErrorBoundary>
      <Component>...</Component>
    </ErrorBoundary>
  );
};

31. Functional Components over Class components: With the introduction of hooks, there’s practically nothing that you can’t do in Functional components as opposed to class components. And functional components are better because:

● They are much easier to read and test because they are plain JavaScript functions.
● You end up with less code. Also, the resulting translated version is comparatively lesser.
● It gets easier to separate container and presentational components because you need to think more about your component’s state if you don’t have access to setState() in your component.
● The React team recommends them stating they provide a performance boost.

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
function Welcome({ name }) {
  return <h1>Hello, {name}</h1>;
}

32. Use memoization techniques: Understanding the render cycle of a component can help us in leveraging the various memoization options provided by React to improve the performance of your application and avoid unnecessary re-rendering (especially when dealing with heavy or logically expensive components & computations). Some memoization techniques include:

● React.PureComponent (for class components)
● memo()
● useCallback()
● useMemo()

However, we can not go about memoizing every component in our application since even memoization uses memory and can be less performant when used incorrectly. Therefore, the only caveat is knowing whether any such memoization technique is needed or not and when to use which.

Here below is an example of a case when using memo is beneficial.

export const App = () => {
  const [userName, setUserName] = useState("John");
  const [count, setCount] = useState(0);
  const increment = () => setCount((count) => count + 1);
  return (
    <>
      <ChildComponent userName={userName} />
      <button onClick={increment}> Increment </button>
    </>
  );
};

const ChildComponent = ({ userName }) => {
  console.log("rendered", userName);
  return <div>{userName}</div>;
};

Although the ChildComponent should render only once because the value of count has nothing to do with the ChildComponent. But, it renders each time you click on the button. A better way would be to change ChildComponent to this:

const ChildComponent = React.memo(({ userName }) => {
  console.log("rendered");
  return <div> {userName}</div>;
});

Disclaimer: The statements and opinions expressed in this article are those of the author(s) and do not necessarily reflect the positions of Jalan Technologies.

Table of Contents

Hire Our Development Experts.




    Want to raise the bar for your organization?

    Subscribe to our newsletter to receive latest insights on how technology can help turn your goals into reality. By subscribing, you’ll get access to thought-provoking articles, case studies, and resources to help accelerate your impact.

    Get the latest updates straight to your inbox

      Related Blogs
      technology consultant
      Business Insights
      All you need to know about technology consultant

      Technology consultant experts like Jalan Technologies, empowers businesses to strategically leverage technology for success. Services encompass strategic planning, system integration,

      Scroll to Top

      Subscribe Now

        Never Miss Another Post