Search justacoding.blog. [Enter] to search. Click anywhere to close.

September 16th, 2022

16 React Tips And Best Practices For Beginners

Here’s a list of 16 React tips and best practices to be aware of when working on your own projects and applications.

In this article, we’ll look at tips that cover multiple different aspects: actually writing the React code, as well as some tips to help with your development workflow (and tooling) in general.

Whilst observing these tips and best practices, the overall aim is to make your code cleaner, more robust and easier to maintain. The tips are particularly suited towards React beginners.

Let’s begin!

1. Use function components instead of class-based ones

Function (or functional) components are generally considered to be more readable than their class-based counterparts.

Here’s a standard class-based component:

class HomePage extends React.Component {
    componentDidMount() {
        console.log("hi")
    }

    render() {
        return <h1>Hello world!</h1>
    }
}

Instead of utilising a class-based React component complete with the various class-based lifecycle methods (such as componentDidMount in this example), this simple component can be rewritten as a function component, instead:

const HomePage = () => {
    useEffect(() => {
        console.log("hi")
    }, [])

    return (
        <h1>Hello world!</h1>
    )
}

This function component uses React’s hooks instead of the class-based lifecycle methods, and it’s syntax and structure is different, as you can see.

For one, this component is literally just a function. It’s not a class that extends React.Component like our class-based example above.

In terms of using hooks in these function components, we’re using the useEffect hook with an empty array as the second argument in this example.

This ensures the code only runs once, on component mount. This is essentially the same behaviour that componentDidMount is providing in the initial class-based snippet.

Writing function components with hooks is, generally speaking, the more modern way to work.

It is of course somewhat down to personal preference. But to most developers, function components are simply more concise, as well as being easier to work with and maintain, and thus it’s considered best practice to use them over their class-based counterparts!

2. Avoid using the index for item keys in a list

If you’ve got any sort of experience working in React, you’ll have likely witnessed the following on numerous occasions:

const PeoplePage = () => {
    const people = [
        { id: 1, name: "Tom" },
        { id: 2, name: "Sarah"}, 
        { id: 3, name: "Jane" }
    ]
    return (
        <ul>
            {people.map((person, index) => {
                return (
                    <li key={index}>{person.name}</li>
                )
            })}
        </ul>
    )
}

The key for each item in the list is assigned based on the items actual position in the list – or its index.

This is generally fine – however, there is one notable exception.

If the list is to be reordered or modified at any point, index-based keys can be problematic. That’s because it’s possible (and likely) that items in the list will be reassigned a different key in subsequent renders.

Consider what happens if we add a new person to the beginning of the list, Robin.

Now Robin will be at index 0, whereas Tom previously held this key. And all other people in the list also have a different key, now, as they each have a different position (or index) within the list due to our new addition!

This can be problematic, as React likes to associate a specific key with a specific item.

So it’s simply good practice to avoid any of these potential issues from the outset, and apply a distinct key per each item in the list where possible instead of relying on the index:

<ul>
    {people.map(person => {
        return (
            <li key={person.id}>{person.name}</li>
        )
    })}
</ul>

Now, regardless of how the list changes, each item still maintains its correct key (and thus, a stable identity).

This is because we’re using the id on the person object, this correlation doesn’t change dependent on the order of the items in the list. And this makes React happy under the hood.

3. Use Fragments instead of divs to avoid adding extra DOM nodes

You may have come across the use of React’s Fragments before.

return (
    <Fragment>
        <h1>Hello world!</h1>
        <h2>I am some nested content!</h2>
    </Fragment>
)

But what are these Fragments and why should you use them?

A Fragment in React is simply the alternative to a div.

A div isn’t always needed – yet including it will still bloat the DOM by adding extra nodes.

If you inspect the elements that comprise your React app within the browser – you’ll notice all of your wrapping div elements here. You may think you need a div, as inner-elements must be wrapped by a common parent.

You do need to wrap these inner-elements in your JSX, but a div isn’t the only (or necessarily the correct) solution to use.

It’s better to cut away these divs when they aren’t really required, and instead wrap your elements with a Fragment.

Utilising Fragments instead of divs (where possible) shows that you have familiarity and are concerned with React’s best practices. Keeping your application clean and bloat-free is one of the keys to fostering concise, easily-maintainable code.

4. Destructure props when passing them to components

Object destructuring in JavaScript is something that’s really useful to understand.

Essentially, you can “unpack” an array (or an object) into its relevant properties.

This can come in useful in React components.

As opposed to doing this:

const Header = (props) => {
    return (
        <h4>Hi, {props.firstName} {props.surname}!</h4>
    )
}

We can do this:

const Header = ({ firstName, surname }) => {
    return (
        <h4>Hi, {firstName} {surname}!</h4>
    )
}

Note how the parameters are passed in this case (firstName and surname – they are inclosed in curly brackets).

The benefit here is that we don’t need to refer to the props object each and every time we want to access one of its properties.

The properties have already been extracted, or destructured, from the original props object when we passed them in to the component. And thus, we can freely use these properties as standalone variables.

Using object destructuring in this way makes your code a little bit more streamlined and concise, which is a good thing for yourself as well as other developers who will work with your code.

It’s worth nothing that this tip applies to any kind of work with JavaScript, object destructuring is not specific to React!

5. Don’t use inline styles

There are of course a variety of ways to go about styling your React components.

Generally speaking, however, you’ll want to avoid inline styling where possible.

Inline styling looks as follows:

<div style={{ backgroundColor: 'red', color: 'black'}}>
    Some content
</div>

The styles are applied directly to the element in question, as you can see.

This works fine, but it’s considered best practice to avoid the use of too many inline styles if possible, for several reasons:

  • You often have to write the same code (or styles) over and over
  • The code isn’t as clean as it could be

For most developers, it’s simply more ideal to define style-related stuff outside of the JSX code. Then apply the classes directly as and when required.

<div style="someClass">Some content</div>

It’s easier to read and work with, and it also leads to less repetitive code in the long run that’s easier to manage.

6. Use custom hooks to extract reusable logic

Creating your own custom hooks allows you to extract your component logic out into easily reusable functions.

If you find that you’re repeating yourself across multiple components — writing the same code in each, this could be a good sign that you should introduce a custom hook instead.

A custom hook looks like as follows:

const useRandomNumber = () => {
    const [randomNumber, setRandomNumber] = useState()

    useEffect(() => {
        setRandomNumber(Math.random())
    }, [setRandomNumber])

    return randomNumber
}

Its name starts with use, and it typically contains core logic that multiple components want to utilise. In this particularly trivial example, the hook just generates a random number. But as you can see, the hook can do basically anything that you’d want to do within a React component (including managing state).

We can easily reuse this hook in any other component where it’s required now like so:

const NumberGenerationTool = () => {
    const randomNumber = useRandomNumber()

    return (
        <p>Your random number is: {randomNumber}</p>
    )
}

Not repeating yourself (“don’t repeat yourself” – see the DRY principle) is a good practice to consider when writing any kind of software; not only when working within React! Hooks facilitate this principle and make it easy to share functionalities across your application.

There are many examples of useful custom hooks for you to consider out in the wild!

7. Use the shorthand method when passing boolean props

Here’s a small tip. Perhaps you don’t know that instead of doing this:

// Based on this component...
const Tag = ({ isHidden }) => {
    return (
        <div>
            {!isHidden ? <p>I am a tag</p> : null}
        </div>
    )
}

<Tag isHidden={true} />

You can do this.

<Tag isHidden />

It’s a small change, but most developers would likely agree that the latter (shorthand) is the cleaner approach.

If the prop in question is a boolean – you can simply pass it via this shorthand method as demonstrated. So just with isHidden instead of isHidden={true}. The prop will subsequently be deemed to be true in the component in question.

If you don’t include a reference to the boolean property on the object as shown here, it’ll be deemed to be false.

So, functionally, this is exactly the same as specifically defining the prop each time it is passed; however, it’s simply cleaner and produces less bloat in the code when handling it via the shorthand approach.

8. Drop the curly braces for simple props

Here’s a similar (somewhat cosmetic) tip.

<Header firstName={"Thomas"} surname={"Robinson"} />

You don’t need the curly braces here. The role of the curly braces in this context is to evaluate whatever expression is contained therein.

If you’re passing a string or a number or something similar, as we are doing here, you don’t need the curly brackets at all. There’s nothing to be evaluated — we’re not passing a JavaScript expression really, just a string in this case.

So it’s simply cleaner to drop the curly brackets altogether in these scenarios, and use them only where required.

<Header firstName="Thomas" surname="Robinson" />

9. Keep components small and modular

This is perhaps one of the more important tips within this article as it concerns a fundamental principle of software design.

When working in React, you should definitely be thinking with this mindset: keep your components simple, clean and modular.

Your application should be built from modular, easily reusable building blocks.

Components that become too large or complex (and thus do too many things at once) are a contradiction to this overall design philosophy.

Once your components reach this state, it will become harder and potentially quite awkward to start extracting them apart into smaller blocks.

With this in mind, it’s best to start off writing small components that do only one thing. Then compose your application from these smaller blocks gradually.

Worth reading: Composition vs Inheritance.

10. Use a linter

This tip isn’t specific to React, but it’s considered best practice to use a linter where possible during development.

An example of a commonly used linter would be ESLint.

The idea here is that you configure the linter within your editor or IDE of choice, and the linter effectively provides a “helping hand” when writing your code.

Not only will the linter typically find problems within your code; it’ll also help you to fix them automatically, too.

In general terms, the linter helps you to catch (and eradicate) bugs or potentially problematic code within your application.

11. Use a prettier

In a similar vein, it’s also good practice to use a prettier within your development workflow.

As the name implies, this facility is simply responsible for assisting with formatting your code and making it… well, prettier!

I’d argue that these benefits aren’t merely cosmetic, though: you’ll save time worrying about spacing and formatting, as well as other conventions your team may be trying to promote, and more time problem solving.

As with the linter, you can configure the prettier to work from within your editor or IDE.

A commonly-used prettier is the aptly-named prettier.

The prettier will help format and clean up your code; the linter will help you catch bugs.

It’s definitely a great idea to use both within your React projects.

12. Avoid creating extra anonymous functions

In the example below, a new function is created on every render:

const SomeComponent = () => {
    const doSomething = () => {
        console.log('hi')
    }

    return (
        <button onClick={() => doSomething()}>Do something</button>
    )
}

This is because of the anonymous function that we’re passing to the onClick handler.

This is not ideal, and should be avoided if possible.

In this case, it’s possible to easily avoid this behaviour.

doSomething doesn’t require any arguments, so there’s no reason to pass it via the anonymous function as we are doing here. It’s wasteful and has an obvious performance implication when you consider that this function is effectively recreated on each render.

Instead, opt to define pass the function as follows:

<button onClick={doSomething}>Do something</button>

Purely by name. Notice the lack of () => { ... }?

This provides an overall benefit to the performance of your application, and thus it’s good practice to follow this approach when you can.

13. Use TypeScript!

This isn’t so much of a tip or a best practice for React, but if you’re at the stage were you’re really comfortable with React and its fundamentals — now might be a good time to look at introducing TypeScript to your development workflow.

Integrating TypeScript within your React application is heavily documented, and TypeScript itself is becoming something of an industry-standard in some respects.

If you aren’t familiar with TypeScript, essentially you’ll be able to use “types” in your JavaScript (React) code.

Instead of declaring a variable like.

let age = 33

You’ll declare it like.

let age: number = 33

This provides a degree of type-safety, so the variable can’t be reassigned a different type elsewhere in your application. One of JavaScript’s weaknesses is how flaky it can be. Not having strictly-typed variables can lead to buggy code and unwanted side-effects.

Here, TypeScript knows that age should always be of type number and thus it will enforce that rule for you.

Ultimately, the idea behind TypeScript in general is to create more robust, less error-prone code.

There are many, many other TypeScript benefits, features and intricacies that simply cannot be covered in a single article.

If you’re interested in taking TypeScript on board, however, the best starting point is, as always, the official documentation.

14. Know when to use reducers

The useReducer hook is an alternative to the useState hook, and it’s best employed once the state in your component starts to become somewhat complex.

A reducer looks like this:

const itemReducer = (state, action) => {
    switch (action.type) {
        case "ARCHIVE":
            return state.map((item) => {
                if (item.id === action.id) {
                    return { ...item, archived: true }
                } else {
                    return item
                }
            })
        case "UNARCHIVE":
            return state.map((item) => {
                if (item.id === action.id) {
                    return { ...item, archived: false }
                } else {
                    return item
                }
            })
        default:
            return state
    }
}

A switch statement is used to determine which action we want to perform on the state object that’s also passed in.

Here’s how this is defined and configured in the parent component:

const Items = () => {
    const [items, dispatch] = useReducer(
        itemReducer,
        {
            id: 1,
            name: "An item",
            archived: true
        },
        {
            id: 2,
            name: "Another item",
            archived: false
        }
    )

    return (
        <ul>
            {items.map((item) => (
                <li key={item.id}>
                    {item.name}{" "}
                    {item.archived ? (
                        <a
                            onClick={() =>
                                dispatch({ type: "UNARCHIVE", id: item.id })
                            }
                        >
                            Unarchive
                        </a>
                    ) : (
                        <a
                            onClick={() =>
                                dispatch({ type: "ARCHIVE", id: item.id })
                            }
                        >
                            Archive
                        </a>
                    )}
                </li>
            ))}
        </ul>
    )
}

So whenever we want to invoke this reducer (itemReducer) to modify our state, we can use the dispatch function that’s returned via the useReducer hook.

Effectively, this is a more structured way to manage your component’s state.

The main benefits become more apparent once your component’s state starts to become more complex, you can delegate the various (potentially intricate) operations to the useReducer hook and then more easily reuse this logic whenever the state needs to change.

In general terms, it’s good practice to simply consider: am I doing things in the most optimal way? If you’re struggling with managing complex state in your application then it may be a sign that you should swap to using reducers instead.

So this tip is all about becoming familiar with the React documentation and recognising the various tools and methods at your disposal!

15. Avoid expensive operations with useMemo

Here’s another tip or best practice that revolves around optimisation.

This time, we’ll look at the useMemo hook.

Let’s say we have an expensive operation or calculation:

const someExpensiveOperation = (num) => {
    const items = []
    for (let i = 0; i < 100000000; i++) {
        items.push(i)
        i++
    }
    console.log(items)
    return items
}

And this operation needs to take place within one of our components:

const Items = () => {
    const [someState, setSomeState] = useState(false)
    const [timesToRun, setTimesToRun] = useState(0)
    const expensiveOperation = someExpensiveOperation(timesToRun)

    const toggleSomeState = () => {
        setSomeState((someState) => !someState)
    }

    const incrementTimesToRun = () => {
        setTimesToRun((timesToRun) => timesToRun + 1)
    }

    return (
        <Fragment>
            <button onClick={toggleSomeState}>Toggle an item in state</button>
            <button onClick={incrementTimesToRun}>Increment</button>
        </Fragment>
    )
}

As it stands now, hitting the Toggle an item in state button will also cause this expensive function to run. In fact, changing any part of the component’s state will trigger a re-render, and thus run this (expensive) function again.

That’s not what we want, though.

This has obvious implications on performance, and we really want to avoid it where possible.

Given that someExpensiveOperation will always return the same result based on the timesToRun we pass in, we should only let this function run once timesToRun actually changes.

At the moment, it’s executing regardless of this.

To achieve this, we can use the useMemo hook:

const expensiveOperation = useMemo(
    () => someExpensiveOperation(timesToRun),
    [timesToRun]
)

Now, toggling the someState boolean won’t cause our function to run again. That’s great, and it’s what we want in this scenario.

This happens because we’re passing a dependency to the useMemo hook — timesToRun.

This ensures that our underlying functionality is only ever executed once timesToRun changes – not on every render as was previously the case.

It’s easy to miss performance problems like the ones described here, so this piece of best practice advice is simply about being mindful of how your components are performing and limiting expensive re-renders wherever possible!

16. Read the documentation

Last but not least, read the documentation (or, less politely, RTFM)!

This one may appear obvious, but it’s amazing how much insight can be gained from casually trawling through documentation.

This tip isn’t specific to React by any means, but when working with any kind of framework or tool in general.

If you find yourself with some free time on your hands and you don’t feel like actually writing code, why not check over the React Top-Level API documentation for instance?

Unless you’re already intimately familiar with the ins and outs of the system in question, it’s highly likely that you’ll find at least a few things of interest or note that you didn’t previous know when trawling the docs.

It’s arguably the case that it’s mainly beginners that fall into the trap (not becoming familiar with documentation), but this tip or piece of best practice is simply about recognising this fact: the official documentation is usually one of the best (or if not, THE best) sources of information at your disposal.

In summary

We’ve gone over few of the more common React tips and best practices that should help you out moving forward.

I hope you’ve gained some insight and have found the article useful.

These tips and best practices for React beginners encompass two main aspects: the actual creation/writing of your code as well as the development workflow configuration. It’s important to have a good grasp on both of these fundamentals if you’re aiming to write robust, clean and maintainable code!

Please be sure to check out our other React articles if you enjoyed this one.

Thanks for reading!

Have any questions? Ping me over at @justacodingblog
← Back to blog