Beginner-level tips and tricks in React

In this tutorial we'll get acquainted with various handy beginner-level tips and tricks in React

By: Ajdin Imsirovic 25 January 2022

< Back to TOC

In this chapter, we’ll cover some simple begineer-level tips and tricks. We had to leave them out from previous chapters for simplicity’s sake, but now there are enough of these tidbits of knowledge that we can bundle them up in a single chapter.

We’ll cover the following topics here:

  • Code organization
  • Passing objects as props
  • Default props
  • Using ternary statements in our components
  • Using fragments
  • Handling events
  • Lifecycle methods in class components

In other words, we’ll demo one of the ways to improve the control over our app’s organization, by grouping all the components into a single folder, named ./components.

Table of contents

  1. Code organization: Reorganizing our reactbootstrap1 app
  2. Passing objects as props
  3. Default props
  4. Using ternary statements in our components
  5. Using fragments
  6. Render styles if a prop is truthy
  7. Handling events
  8. Simplifying a complex component (without props)
  9. Simplifying a complex component (with props)
  10. Updating the Footer to accept a copyright prop
  11. Extracting the Copyright component from the Footer component
  12. Don’t display a component if a prop is not passed in the parent
  13. Conditionally returning a different string than the one passed in from a prop
  14. Render prop if truthy using the AND operator
  15. Conclusion

Code organization: Reorganizing our reactbootstrap1 app

Let’s look at the current state of things in our app.

A screenshot of our current file structure

Now, we’ll add a new sub-folder inside the ./src folder, and we’ll name it /components.

Next, we’ll move all the components into this nested sub-folder, namely, the following files:

  • Footer.js
  • Jumbotron.js
  • Jumbotron2.js
  • MainMenu.js
  • Product.js

We’ll also need to update imports in App.js, for example:

import Footer from './components/Footer';

Here’s a screenshot of our VS Code showing the updates to App.js and to our file structure.

A screenshot of our updates

Passing objects as props

Let’s have a reminder of props destructuring (used in Product.js):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Product = ({ name, description }) => {
    return (
        <div className="card">
            <div className="card-body">
                <h5 className="card-title">{name}</h5>
                <p className="card-text">
                    {description}
                </p>
                <a href="#" className="btn btn-primary">Go somewhere</a>
            </div>
        </div>
    );
}

export default Product;

The name and description values of the props object are destructured right in the parameter passed to the Product function. This allows us to then use {name} and {description} instead of a more complex approach, namely this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Product = (props) => {
    return (
        <div className="card">
            <div className="card-body">
                <h5 className="card-title">{props.name}</h5>
                <p className="card-text">
                    {props.description}
                </p>
                <a href="#" className="btn btn-primary">Go somewhere</a>
            </div>
        </div>
    );
}

export default Product;

Regardless of whether we’ve used the props object or direct props destructuring in our components, when we’re passing values via props in parent components, everything stays the same. With the Product.js component, in App.js, that looks as follows:

1
2
3
4
5
<Product
    key={prod.name}
    name={prod.name}
    description={prod.description}
/>

But what happens if we want to pass objects as prop values in parent components? We’d do it like this:

<Product
    {{ key: 1, name: "T-shirt", description: "Cool t-shirt" }}
/>

So, we’re using regular curly braces, and then another, inner set of curly braces for our object.

Default props

There are situations where we’d like our props to have some default values. This is achieved using defaultProps, as follows.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Product = ({name, description}) => {
    return (
        <div className="card">
            <div className="card-body">
                <h5 className="card-title">{name}</h5>
                <p className="card-text">
                    {description}
                </p>
                <a href="#" className="btn btn-primary">{cta}</a>
            </div>
        </div>
    )
}
Product.defaultProps = {
    cta = "Go somewhere";
}
export default Product;

Above, we’re defining the cta (“call-to-action”) prop to be used as the button message.

Nothing changes in App.js:

1
2
3
4
5
<Product
    key={prod.name}
    name={prod.name}
    description={prod.description}
/>

The result is that each of our buttons will now have a dynamic cta value.

Using ternary statements in our components

Continuing from the previous example, let’s make our cta variable even more dynamic, through the use of a ternary statement.

I’ll reuse a recipe from my book A Better Way to Learn JavaScript: Useful Snippets (this is the third book in my book series on JavaScript).

Let’s first examine the recipe on its own:

const randomBool = Math.random() > 0.5 ? true : false;

The above code snippet randomly returns either a true or false Boolean value.

We’ll now use this ternary logic to make our cta more dynamic.

Back in Product.js, here’s the updated code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Product = ({name, description}) => {
    let cta = (Math.random() > 0.5) ? "Go somewhere" : "Go anywhere";
    return (
        <div className="card">
            <div className="card-body">
                <h5 className="card-title">{name}</h5>
                <p className="card-text">
                    {description}
                </p>
                <a href="#" className="btn btn-primary">{cta}</a>
            </div>
        </div>
    )
}
export default Product;

On the first attempt at bundling the app using the above component, the buttons showed the following output.

The result of using a ternary in a component

Using fragments

We’ll stick with our ternary setup and make it even more interesting with fragments.

Here’s how it works: Instead of putting out a short string of text in our ternary, we’ll add an entire structure, surrounded by parentheses.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React from "react";

const Product = ({name, description}) => {
    return (Math.random() > 0.5)
    ? (
        <React.Fragment>
            <div className="card">
                <div className="card-body">
                    <h5 className="card-title">{name}</h5>
                    <p className="card-text">
                        {description}
                    </p>
                    <a href="#" className="btn btn-secondary">Go somewhere</a>
                </div>
            </div>
        </React.Fragment>
    )
    : (
        <React.Fragment>
            <div className="card">
                <div className="card-body">
                    <h5 className="card-title">{name}</h5>
                    <p className="card-text">
                        {description}
                    </p>
                    <a href="#" className="btn btn-primary">Go anywhere</a>
                </div>
            </div>
        </React.Fragment>
    )
}

export default Product;

Note that we need to wrap everything in <React.Fragment> to make this work.

Here’s a result.

Building our cards with React fragments

There is even shorthand syntax for <React.Fragment>, and it looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React from "react";

const Product = ({name, description}) => {
    return (Math.random() > 0.5)
    ? (
        <>
            <div className="card">
                <div className="card-body">
                    <h5 className="card-title">{name}</h5>
                    <p className="card-text">
                        {description}
                    </p>
                    <a href="#" className="btn btn-secondary">Go somewhere</a>
                </div>
            </div>
        </>
    )
    : (
        <>
            <div className="card">
                <div className="card-body">
                    <h5 className="card-title">{name}</h5>
                    <p className="card-text">
                        {description}
                    </p>
                    <a href="#" className="btn btn-primary">Go anywhere</a>
                </div>
            </div>
        </>
    )
}

export default Product;

The effect is the same, but we do need to type less, and our code looks a bit tidier.

Render styles if a prop is truthy

Another useful approach is to render styles based on whether a prop is truthy.

For this, we’ll re-use a piece of code we’ve had in a previous section:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Product = (props) => {
    return (
        <div className="card">
            <div className="card-body">
                <h5 className="card-title" style="{{display: props.name ? "block" : "none"}}>{props.name}</h5>
                <p className="card-text">
                    {props.description}
                </p>
                <a href="#" className="btn btn-primary">Go somewhere</a>
            </div>
        </div>
    );
}

export default Product;

In the code above, if the props.name prop exists, the attached style will be display: block. Otherwise, it will be display: none. Effectively, this is the same as if we were using the && operator approach.

Handling events

Let’s continue working on our reactbootstrap1 app to deal with a simple event.

We’ll add an event handling functionality which will take care of the Jumbotron component’s button. Specifically, any click events happening on that button.

We’ll achieve that using React hooks. Using hooks, we can work with state and deal with events.

So, let’s revisit the Jumbotron.js file, and start updating it by adding an import of useState:

1
2
3
4
5
6
7
import { FaAndroid } from "react-icons/fa";
import {useState} from 'react';

const Jumbotron = () => {
.
.
.

Note: The three dots are replacing the rest of the existing code in the Jumbotron.js component.

We imported the useState hook so that we can work with state in our component. In turn, this means that we need to setup a way to track state in our component. We’ll do that by adding a state-tracking variable. The following “weird-looking” line will be the first line at the very top of the Jumbotron function:

let [btnToggle, setBtnToggle] = useState(false);

The first value (btnToggle) is the name of the variable. The second value (setBtnToggle) is the variable modifier - something to react to state changes.

So, what’s happening above is: we’re utilizing the useState hook to set the value of the btnToggle variable to false, and to deal with events using setBtnToggle.

So now, let’s use fragments again, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import { FaAndroid } from "react-icons/fa";
import {useState} from 'react';

const Jumbotron = () => {
    let [btnToggle, setBtnToggle] = useState(false);
    return (
        <div className="p-5 mb-4 bg-light rounded-3">
            <div className="container-fluid py-5">
                <h1 className="display-5 fw-bold">
                    <FaAndroid />
                    Custom jumbotron
                </h1>
                {
                    btnToggle &&
                    <p className="col-md-8 fs-4 m-auto">
                        Using a series of utilities, you can create
                        this jumbotron, just like the one in previous
                        versions of Bootstrap. Check out the examples
                        below for how you can remix and restyle it
                        to your liking.
                    </p>
                }
                <button className="btn btn-primary btn-lg" type="button">
                    Details
                </button>
            </div>
        </div>
    );
}

export default Jumbotron;

So, what we’re doing above is, we’re surrouding our fragment in {} brackets. Contrast that with the <> delimiters: the difference is that the btnToggle variable value will be processed when using { and } fragment delimiters, while with <> the value will be printed as plain text on the screen, including the && operator and the rest of the code.

Now, to make our currently hidden paragraph toggle, we’ll add an event listener to our button, as follows:

1
2
3
4
5
6
7
<button
    onClick={ () => {} }
    className="btn btn-primary btn-lg"
    type="button"
>
    Details
</button>

There’s a few of things to unpack in the above code:

  • first, the onClick attribute is assigned to the result of running an expression (everything between the opening and closing {} braces)
  • second, we’re using the onClick with the capital C, which is React’s way to implement the built-in onclick event handling HTML attribute
  • third, we’re passing, in between the { and } curly braces, the arrow function to be ran (obviously, currently, the arrow function is empty)

The arrow function that’s invoked in the onClick attribute will be very simple:

onClick={ () => {setBtnToggle(!btnToggle)} }

That’s all there is to it. Now the click on the button in the jumbotron component will toggle the Boolean value of our state tracking variable, and that will consequently show or hide the paragraph above that button. Here’s the full code of Jumbotron.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { FaAndroid } from "react-icons/fa";
import {useState} from 'react';

const Jumbotron = () => {
    let [btnToggle, setBtnToggle] = useState(false);
    return (
        <div className="p-5 mb-4 bg-light rounded-3">
            <div className="container-fluid py-5">
                <h1 className="display-5 fw-bold">
                    <FaAndroid />
                    Custom jumbotron
                </h1>
                {
                    btnToggle &&
                    <p className="col-md-8 fs-4 m-auto">
                        Using a series of utilities, you can create this
                        jumbotron, just like the one in previous versions of
                        Bootstrap. Check out the examples below for how you
                        can remix and restyle it to your liking.
                    </p>
                }
                <button
                    onClick={ () => {setBtnToggle(!btnToggle)} }
                    className="btn btn-primary btn-lg"
                    type="button"
                >
                    Details
                </button>
            </div>
        </div>
    );
}

export default Jumbotron;

We’re now handling button clicks in our React app.

Note that React uses these custom events such as onClick, onChange, onSubmit, etc. These are all known as Synthetic Events.

Simplifying a complex component (without props)

Let’s revisit the reactbootstrap1 app’s Footer component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const Footer = () => {
    return (
      <div className="container">
        <footer className="py-3 my-4">
          <ul className="nav justify-content-center border-bottom pb-3 mb-3">
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                Home</a></li>
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                Features</a></li>
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                Pricing</a></li>
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                FAQs</a></li>
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                About</a></li>
          </ul>
          <p className="text-center text-muted">© 2021 Company, Inc</p>
        </footer>
      </div>
    );
  }

export default Footer;

This component is relatively straightforward, but perhaps still complex enough so that we can simplify it and practice how to split a single component into a number of simpler components.

First, let’s think about the structure of the Footer component. It has:

  • A footer menu
  • A copyright notification

Let’s split these two sections into separate components.

We’ll extract the footer menu into a new component: we’ll save it as a brand new file, named SecondaryMenu.js.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const SecondaryMenu = () => {
    return (
        <ul className="nav justify-content-center border-bottom pb-3 mb-3">
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                    Home
                </a>
            </li>
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                    Features
                </a>
            </li>
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                    Pricing
                </a>
            </li>
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                    FAQs
                </a>
            </li>
            <li className="nav-item">
                <a href="#" className="nav-link px-2 text-muted">
                    About
                </a>
            </li>
        </ul>
    );
}

export default SecondaryMenu;

Now we’ll include it inside the Footer.js component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import SecondaryMenu from './SecondaryMenu';

const Footer = () => {
  return (
    <div className="container">
      <footer className="py-3 my-4">
        <SecondaryMenu />
        <p className="text-center text-muted">© 2021 Company, Inc</p>
      </footer>
    </div>
  );
}

export default Footer;

Simplifying a complex component (with props)

Things are often going to be a bit more complex than what we had in the previous example.

We’ll use the fact that we still haven’t extracted the Copyright component from the Footer component, and thus we can first update the existing Footer component so that it accepts prop data to be passed into it from the app component.

Thus, our plan is as follows:

  1. First, we’ll update the Footer to accept a copyright prop.
  2. Second, we’ll extract the Copyright component from the Footer component, then add it and demo how that affects the prop being passed in.

Let’s start with the App component, and update it to this (on line 31):

<Footer copyright="(c) 2021 THE Company, Inc" />

We’ve updated the company name from Company to THE Company - this is a simple reassurance that we’ve indeed changed something in our code - which will end up in the browser and thus confirm our updates.

The rest of the App component is unchanged.

Next, let’s open the Footer component, and update it to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
...

const Footer = (props) => {
  .
  .
  .
  .
        <p className="text-center text-muted">{props.copyright}</p>
  .
  .
  .
}

...

Note: The dots are “pseudo-code” replacement for the actual, unchanged code - this allows us to highlight the actual changes in the above snippet.

Thus, the only thing we’ve changed in our code is passing the props object to the Footer function, and using the {props.copyright} in the p element in JSX syntax.

Now we’re ready to extract another component from our Footer.

Let’s make another component and name it Copyright.js:

1
2
3
4
5
const Copyright = (props) => {
  return <p className="text-center text-muted">{props.copyright}</p>;
};

export default Copyright;

Additionally, let’s update the Footer.js component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import SecondaryMenu from './SecondaryMenu';
import Copyright from './Copyright';

const Footer = (props) => {
  return (
    <div className="container">
      <footer className="py-3 my-4">
        <SecondaryMenu />
        {/* <p className="text-center text-muted">{props.copyright}</p> */}
        <Copyright copyright={props.copyright} />
      </footer>
    </div>
  );
}

export default Footer;

Notice the funny looking line of code above the Copyright component:

{/* <p className="text-center text-muted">{props.copyright}</p> */}

That’s a JSX comment.

Anyway, what we did in the Footer component is: we’ve included the Copyright component, replacing the original line of code that, before the update, read:

<p className="text-center text-muted">{props.copyright}</p>

Passing props (i.e state) like this can get messy. That’s why using Redux to manage state is so popular. But we won’t get into it just yet.

Don’t display a component if a prop is not passed in the parent

Here’s another important beginner-level tip.

Let’s say that for some reason our Footer component doesn’t pass the copyright prop - someting that our Copyright component expects.

In other words, let’s say that on line 10 of our Footer component, we have this:

<Copyright />

… instead of this:

<Copyright copyright={props.copyright} />

How do we make our app more “sturdy”, meaning: instead of throwing an error, the app still works, but it simply doesn’t show the Copyright component.

Put differently, under a certain condition, we want to not render anything from our Copyright component, and we also want to keep our app running rather than throwing an error.

We’ll do it by returning null in our Copyright.js component, if the conditions are right:

1
2
3
4
5
6
const Copyright = (props) => {
  if (!props.copyright) return null;
  return <p className="text-center text-muted">{props.copyright}</p>;
};

export default Copyright;

Now, even if we don’t pass the previously compulsory prop inside the Footer component:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import SecondaryMenu from './SecondaryMenu';
import Copyright from './Copyright';

const Footer = (props) => {
  return (
    <div className="container">
      <footer className="py-3 my-4">
        <SecondaryMenu />
        {/* <p className="text-center text-muted">{props.copyright}</p> */}
        {/* <Copyright copyright={props.copyright} /> */}
        <Copyright />
      </footer>
    </div>
  );
}

export default Footer;

… our app will not throw an error, but rather continue working (only without the copyright component showing in the browser).

All we had to do was add:

if (!props.copyright) return null;

Conditionally returning a different string than the one passed in from a prop

Let’s continue improving the Copyright component by adding another piece of logic.

This time we’re interested in the copyright prop’s text length. Let’s say that unless the length of text is longer than 10 characters, we want to show a different message, for example, “all rights reserved”.

Here’s the Copyright.js file after the update from the previous section:

1
2
3
4
5
6
const Copyright = (props) => {
  if (!props.copyright) return null;
  return <p className="text-center text-muted">{props.copyright}</p>;
};

export default Copyright;

We’ll start by adding a pair of parentheses after the return keyword:

1
2
3
4
5
6
7
8
9
const Copyright = (props) => {
  if (!props.copyright) return null;
  return (

  )
  <p className="text-center text-muted">{props.copyright}</p>;
};

export default Copyright;

Next, we’ll move the paragraph back into the return:

1
2
3
4
5
6
7
8
const Copyright = (props) => {
  if (!props.copyright) return null;
  return (
      <p className="text-center text-muted">{props.copyright}</p>
  )
};

export default Copyright;

Now we need to add a condition: “If x then show A, otherwise show B”.

We’ll do that using a ternary expression:

1
2
3
4
5
6
7
8
const Copyright = (props) => {
  if (!props.copyright) return null;
  return (
      {x ? A : <p className="text-center text-muted">{props.copyright}</p> }
  )
};

export default Copyright;

What’s happening above is: we’re giving a “formula” for the ternary, i.e we’re translating the “If x then show A, otherwise show B” condition, as explained earlier.

So, what’s our “x”? And why is the entire string in curly braces?

First of all, “x” is the condition, and the string is in curly braces because that’s how we add an expression inside JSX.

An expression is something that returns a value, and what we’re trying to establish is whether our prop’s text is less than 10 characters long and return a value based on that.

Thus:

{props.copyright.length < 10 ? A : B}

So, if the length of the copyright prop is less than 10 characters, whatever represents “A” will be run. Otherwise, whatever is in B will be run, which is our regular paragraph we already used:

<p className="text-center text-muted">{props.copyright}</p>

That means that the code of the Copyright component should now look like this:

1
2
3
4
5
6
7
8
9
10
const Copyright = (props) => {
  if (!props.copyright) return null;
  return (
      {props.copyright.length <10 ?
      null :
      <p className="text-center text-muted">{props.copyright}</p>}
  )
};

export default Copyright;

To avoid errors, we also need to wrap everything inside fragments, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const Copyright = (props) => {
    if (!props.copyright) return null;
        return (
            <>
            {props.copyright.length < 10 ?
            null :
            <p className="text-center text-muted">
                {props.copyright}
            </p>}
            </>
        )
    };

export default Copyright;

Now, to test it out, let’s pass in a short copyright to App.js:

<Footer copyright="123456789" />

Obviously, the copyright prop has less than 10 characters, and thus it will not render any copyright.

But there’s a slight wrinkle here: it’s betetr to return anything else other than null, because passing any kind of string will give us an immediate cue that our update works. Thus, here’s the full updated Copyright component:

1
2
3
4
5
6
7
8
9
10
11
12
const Copyright = (props) => {
    if (!props.copyright) return null;
        return (
            <>
            {props.copyright.length < 10 ?
            "all rights reserved" :
            <p className="text-center text-muted">{props.copyright}</p>}
            </>
        )
    };

export default Copyright;

Additionally, we mustn’t forget to pass the prop from both the App.js:

<Footer copyright="123456789" />

… and the Footer.js:

<Copyright copyright={props.copyright} />

That’s it, we’re successfully displaying a string that is different than the one we passed in the prop, based on some custom logic we chose to use.

Render prop if truthy using the AND operator

To render a component if truthy, we can use the && operator.

Let’s use the example from the previous section as a starting point:

const Copyright = (props) => {
  if (!props.copyright) return null;
  return <p className="text-center text-muted">{props.copyright}</p>;
};

export default Copyright;

Instead of returning null like above, we can use the && operator as follows:

const Copyright = (props) {
    return (
        {props.copyright && <p className="text-center text-muted">{props.copyright}</p>}
    )
}

In the above code snippet, inside the return statement, we first check if props.copyright is truthy, because the && operator requires that both the code on its left side and the code on its right side is truthy.

If that’s the case, then it will return:

<p className="text-center text-muted">{props.copyright}</p>

However, if props.copyright is falsy, it won’t return anything.

Conclusion

That’s it for this chapter. In the next one we’ll discuss some differences in component syntax in React.

< Prev lesson Next lesson >

Feel free to check out my work here: