How To Build Dynamic React Components With JSX (Part 2)

by kleamerkuri
Build dynamic React components with JSX.

Once you start a React project, it’s good to lay out a plan of what pieces the application you’re building requires.

React groups UI elements into components that are reusable building blocks in the UI. The number of components depends on how granular you’d like to go.

You don’t have to reuse React components! But the beauty of reusing them lies in keeping a clean, DRY code-base.

For this project, I’m grouping elements into the following components:

  1. Form component with two inputs for name and age
  2. List component to display entered name and age
  3. Modal component for notifying a user of an error
  4. Re-usable button and wrapper components to keep code DRY

I’m not bothering with styles at this second part since we’ll be tackling adding styles to React components in the next post!

After planning the different pieces of the application, we’re ready for part two: building React components.

Cleaning up the default React files

In the previous post, we saw how create-react-app helped generate all necessary boilerplate React files to start a project.

But we don’t need a lot of the default styles or content such as those found on the App component.

So, let’s take a look at the default App component and clean it up a bit.

This means getting rid of the imported logo as well as basically everything inside of the wrapper div 😬

Modifying first React component.

I replaced all that was removed with a simple H2 to demo what’s left after removing the defaults.

What is a React component?

A React component in itself is a custom HTML element created using a JavaScript function that returns JSX (a React-specific syntax that’s a mix of JavaScript and HTML).

Here are a few things to highlight about a React component:

  • It must start with an uppercase character so React can identify it as a custom component.
  • You can write it as a self-closing element if there’s no content between the tags.
  • It must return one root statement that is wrapped in parenthesis when spanning multiple lines.

Note: Naming Conventions

In JavaScript, there are different case types with space being a reserved character.

  1. Snake case: Each word is separated by an underscore, all lower or upper case
  2. Kebab case: Each word is separated by a dash, all lowercase
  3. Camel case: First letter is lower case, then capitalize the first letter of each word
  4. Pascal case: First letter of every word is capitalized (otherwise known as Title case)

React components tend to follow the Pascal case naming convention.


How to create a React component

I’ll store each component in an individual file located in a components directory.

Tip 😌
Group components in subdirectories within the components directory to keep code files organized. I do this with the UI directory that will hold reusable elements like wrappers and buttons.

Splitting components is a great way to keep code manageable! There’s no hard-set rule on what determines a split, though, it’s typical to split a component when it has its own styles and/or logic.

An example would be shell components which are re-usable wrappers that have content between the tags. We’ll be creating such a component in a bit so stay tuned 🙃

The process of creating a component can be summed up as:

  1. declare a component
  2. export the component as default
  3. import the component in the root component (i.e. <App />) or parent
  4. use the component as you would an HTML element

Refresher: HTML For Beginners – A Step-by-Step Guide To HTML Tags

Dynamic data and props

In React, props are properties added to custom components.

Example: <CustomItem title="The THT Manifesto of Coding Awesomeness" />

In the example, CustomItem is the custom component and title is the property. The value of title is accessible in CustomItem .

Following this pattern, we’ll be passing data via props in two basic steps:

  1. Assigning properties to custom components
  2. Accessing the properties in the respective component files

Step two is made easy by default as React ensures we get one parameter in every component as an object that holds all received values as props.

Tip: You can name the default parameter whatever you want, but usually it’s standardized as props for sanity check.

In short, you pass data across components via attributes. These attributes are stored as key-value pairs in a default object that React ensures is passed as a parameter to every component.

Building the React components

In the next few steps, we’ll build most of the components necessary for the project application.

1) Re-usable components

These are going to be the button and card components.

Card component

I’ll be using a custom container to act as a wrapper for divs with common styles so I’m creating a simple card component in Card.js.

const Card = (props) => {
  return <div>{props.children}</div>;
};

export default Card;

Note: You don’t need to call it “Card” – it can be anything you fancy. But convention calls wrappers such as these “cards” so we’ll be angels and stick with it 😇

The Card component will wrap the form and list components, rendering the nested components using { props.children } .

As a little React syntax breakdown:

  • props: An object with built-in and custom properties provided by default by React.
  • children: A built-in property on the props object with a value that is equal to the content between the tags.
  • curly braces: Signal injection of JavaScript into HTML.

Note: Adding Instance-specific Styles

Any styles on the div returned by the Card component will be common across all Cards. But, let’s say, we want a specific style added to a particular Card in one of our components.

What do we do in such a case?

It’s really an easy solution summed up in three steps:

  1. Define the style on the stylesheet of said component
  2. Assign the style in the className attribute on the Card component
  3. Add className through props in the style attribute on the built-in HTML div in Card.js

Hey!
Don’t worry too much about styles right now since we’ll get to them on part three with plenty of examples. But it’s good to have some context in mind for all the curious minded individuals 🫵

Keep in mind, you don’t need to create the Card component.

However, since I know the wrappers for the form and list components share the same function and styles, it makes sense to create a reusable element.

Button component

Continue by creating the second re-usable component, the button, in Button.js.

const Button = (props) => {
    return <button type={props.type}>{props.children}</button>;
  };

  export default Button;

In addition to rendering nested elements using props.children I’m pulling the value of props.type and assigning it as the type for the button element.

The value of props.type comes from the type attribute that I expect to assign to the <Button></Button> JSX element.

Assigning type is a way to control the type of a custom button component since the HTML type attribute does not apply to JSX.

If I were to use the HTML type attribute, it can only be applied in Button.js on the returned button element. But doing so means hardcoding a type as, for example, button or submit.

Hardcoding won’t do us much good, especially with a reusable component.

Yet, assigning the button element a dynamic type using props.type allows the type to change based on what we assign to a button component.

For fun: How To Make An Animated Button With CSS

2) Form component

Create the form component, <Form /> , which will render a form with two input fields and a button.

import Button from "./UI/Button";

const Form = (props) => {
  return (
    <form>
      <label for="name">Name</label>
      <input type="text" id="name" name="name" />

      <label for="age">Age</label>
      <input type="number" id="age" name="age" />

      <Button type="submit">Add User</Button>
    </form>
  );
};

export default Form;

Breaking down the code:

  • import the re-usable Button component created earlier
  • define a form element with two input fields
  • create a button with a type of submit
  • export the Form function

Note: If you forget to export your functional component, React will throw an error telling you just that!

These are modular JavaScript files. To use the variables or functions defined in one, you need to make sure you export and then import into the file you’d like to use them on.

Read: An Easy Guide to HTML Forms For Beginners

Tip 🪲
Replace the for attribute on the label with htmlFor, a React alternative to avoid the “invalid DOM property” error. Why? Because for, just like class, is a reserved word!

Let’s see what we created by rendering the Form. To do this, we must:

  1. import Form in App.js (the main component that gets rendered in the root element via index.js)
  2. include the Form component inside of the return statement of the App component
import Form from "./Components/Form";
import Card from "./UI/Card";

function App() {
  return (
    <div className="App">
      {/* Form component */}
      <Card>
        <Form />
      </Card>

      {/* List component */}
    </div>
  );
}

export default App;

Tip 🤓
Above, I’m commenting in JSX to notate my work. Using comments in code is good practice as it makes code readable both by yourself and other developers. I typically use comments to structure my work.

Read: This Is How To Comment In HTML, CSS, And JavaScript

Creating a form component React.

3) List component

Break down the List component into two components:

  • List: A container component that will hold the loop logic doing the rendering of each ListItem.
  • ListItem: A presentational component whose main purpose is to render the name and age as they’re passed down.

Then create a List directory to organize the components that make up the list.

Refresher: HTML Lists & Images – An Easy Guide To HTLM For Beginners

Jumping real quick onto App.js, add faux data by creating a list of objects like so:

const USERS = [
  {
    name: 'THT',
    age: 1.5
  }
];

The USERS array will be our database proxy where we’ll store new users and pull their data for display in the rendered list.

Note: USERS is located outside of the App function!

Pass USERS on to the List component like so <List users={USERS} /> . Our App.js now looks like this:

import Form from "./Components/Form";
import List from "./Components/List/List";
import Card from "./Components/UI/Card";

import "./App.css";

const USERS = [
  {
    name: "THT",
    age: 1.5,
  },
];

function App() {
  return (
    <div className="App">
      {/* Form component */}
      <Card>
        <Form />
      </Card>

      {/* List component */}
      <Card>
        <List users={USERS} />
      </Card>
    </div>
  );
}

export default App;

Back in List.js do:

import ListItem from './ListItem';

const List = props => {
    // props.users is [{}]
    let {name, age} = props.users[0];

    return (
        <ul>
            <ListItem name={name} age={age} />
        </ul>
    )
}

export default List;

What I’m doing here is destructuring the name and age properties from the single object in the USERS array that was passed down via props.

I’m selecting only the first object for demonstration purposes since it’s the only piece of data we have there anyways. This will change later on when we loop over the USERS array.

Note: You don’t have to destructure from props, but it definitely shortens what you have to type out (aka props.users[0].name, as example).

The List component renders an unordered list containing each ListItem component which I define in ListItem.js as:

const ListItem = props => {
    return <li>{props.name} ({props.age} years old)</li>
}

export default ListItem;

Rather straightforward here as we’re simply displaying the age and name in a list item element.

Notice the bits extracted from props – the JavaScript parts – are encased in curly braces!

Don’t forget to (a) import List into App.js and (b) return it wrapped in a Card component!

This is what the browser renders:

Rendering data in a list component React.

4) Modal component

I give the modal a nested structure that consists of a background overlay (parent wrapper) and the modal itself (a Card component).

Inside the modal, we have a header that acts as a modal title and a body that displays the error message.

Although the title doesn’t change, the error message in the body does change depending on which input throws the error. Here, I include some text placeholder that I’ll update later on.

import Button from "./UI/Button";
import Card from "./UI/Card";

const Modal = props => {
    return (
        <div className="modal_overlay">
            <Card>
                <div className="modal_header">
                    <p>Invalid input</p>
                </div>
                <div className="modal_body">
                    <p>Error message goes in here.</p>
                </div>
                <Button type="button">Close</Button>
            </Card>
        </div>
    )
}

export default Modal;

Next, I import the Modal component in Form.js as it’s where I’m expecting to define the logic for having the modal display.

No need to worry about the logic, we’ll tackle that at a later step! For now, I’m simply rendering Modal in Form.js so we can see what we have.

Complete React components.

How to troubleshoot a React app

To troubleshoot this React project, I’ll use the React DevTools Chrome extension. Once installed, access it via a dev tool inspect tool like Google Inspect.

Clicking on Components provides a tree of custom React components (in a non-compiled form) and their respective information.

Troubleshooting React app extension.

Super helpful considering we can’t view our components in their raw JSX form in the DOM as they get compiled for the browser.

Tip 🪲
To identify bugs in the code, use the browser developer tools by setting a breakpoint on the select file and running the debugger. This way, you can step through the code line by line!

With this, part two is now complete. I’ll see ya in part three where we’ll add styles to our React components!

Adeus 🙃

Related Posts