Skip to main content

React Fundamentals - JSX & Props

Learning Objectives

Students Will Be Able To:
Explain the Use Case of JSX
Explain the Benefits that JSX Provides
Use JSX to Define a Component's UI
Render JS Expressions Into the DOM Using JSX
Include Props in JSX
Render an Array of Components
Style Components Using the className & style Prop

Road Map

  1. Setup
  2. JSX - What & Why?
  3. Review: JS Expressions
  4. Writing JS Expressions Into the DOM Using JSX
  5. JSX Itself is a JS Expression!
  6. What Are Props?
  7. Properly Rendering Arrays of Components
  8. Props for React Elements
  9. Intro to Styling React Elements
  10. Essential Questions
  11. Bonus Practice Exercise

Videos

Video 📹 Link

1. Setup

This lesson builds upon the "React To-Do" sandbox we created in the previous lesson in codesandbox.io:

2. JSX - What & Why?

What?

  • JSX is an XML-like syntax extension to JavaScript. XML is short for extensible markup language and JSX is one of those extensions - just like HTML is.

  • Created by Facebook for use in React.

  • It provides a way to concisely define tree structures with attributes - perfect for defining a DOM tree!

  • It is transpiled into pure JavaScript.

    ❓ What is transpiling

    Transpiling converts source code of one language into source code of another language. They are often referred to as source-to-source compilers. Compilers on the the other hand, convert source code into a form of executable code.


Why JSX?

  • It's simple and clear. Compared to pure JavaScript, JSX provides a more concise and better way to define a UI.

  • JSX resembles HTML, which allows us to more easily visualize the UI its JavaScript will create.

  • 99.99% (a guess) of React apps are developed today using JSX to define the UI, not vanilla JS.

3. Review: JS Expressions

Before we learn how to render the result of a JS expression within JSX, let's review what JS expressions are...

JavaScript expressions evaluate to a single value/thing.

They can be used wherever a value is used, for example:

  • Assigned to a variable
  • Provided as an argument to a function call
  • Returned from a function

Statements on the other hand, perform actions. A program's statements such as if statements and for/while loops are not expressions.

4. Writing JS Expressions Into the DOM Using JSX

To write the result of a JS expression into the DOM within the JSX, we simply need to enclose the expression within curly braces.

Let's experiment by typing the the following within <ToDoList> in our sandbox and observe the output:

Live Editor
function ToDoList() {
  const str = "SEI";
  const score = 94;
  return (
    <ul>
      <li>Number: {score}</li>
      <li>String: {str}</li>
      <li>Math.random(): {Math.random() * 100}</li>
      <li>Template Literal: {`${str} Rocks`}</li>
      <li>Ternary: {score > 90 ? "A" : "B or less"}</li>
      <li>
        Booleans, null & undefined: {true}
        {false}
        {null}
        {undefined}
      </li>
      <li>Logical &&: {score > 90 && <div>Got an 'A'!</div>}</li>
    </ul>
  );
}
Result
Loading...

The fact that React does not output the value of booleans, null or undefined can really come in handy, e.g., when using the logical && scenario for writing out an expression or not.

Objects and most of the built-in Objects (Date, Functions, RegExp, etc.) cannot be rendered as is - just their properties can be rendered. The practical exception are Arrays which we'll look at shortly.

5. JSX Itself is a JS Expression

Developing real-world React apps requires tooling to:

  • Transpile the JSX into pure JS
  • Import CSS
  • Bundle the JS modules

The most popular transpiler for JSX is Babel. The following shows what a JSX expression transpiles into:

As you can see, JSX transpiles into a function call: React.createElement(...), which returns an object used by the React library.

Because JSX results in a function call that returns an object, JSX is a JavaScript expression! Thus, JSX can be assigned to variables, used in ternary expressions, etc.!

A Component Must Return a Single Root JSX Node

Because a function can only return a single expression, we must be sure to return a single root JSX node using the return statement.

For example, the following will fail to compile:

Live Editor
function Whoops() {
  return (
    <h1>Whoops</h1>
    <h2>No Dice!</h2>
  );
}
Result
Loading...

The function above is attempting to return two things, i.e., two objects which just isn't possible in JavaScript (or any other popular programming language).

The only solution used to be to wrap the sibling components with a single React Element such as a <div>:

Live Editor
function GoodToGo() {
  return (
    <div>
      <h1>Good To Go</h1>
      <h2>Dice Please!</h2>
    </div>
  );
}
Result
Loading...

The downside of the above approach is that this results in an extra, and perhaps unwanted <div> rendered to the DOM which could impact layout/styling.

A better alternative if you don't want or need an extra wrapping React Element is React Fragments added in version 16.2 of React:

Live Editor
function GoodToGo() {
  return (
    <>
      <h1>Good To Go</h1>
      <h2>Dice Please!</h2>
    </>
  );
}
Result
Loading...
tip

👀 The <> </> syntax is shorthand for <React.Fragment> </React.Fragment>

👉 You Do - Render an Array (3 minutes)

  1. Define an array named misc before the return statement that has the following elements:
  • A string of your choosing
  • A number of your choosing
  • A boolean of true or false
  • A React Element, e.g., a <div> with some content
  1. In ToDoList.jsx Add an <li> React Element and render the array as its content.

As you can see, React can render arrays. In fact, it's very common to render arrays of components, which we'll do in a bit.

6. What Are Props?

Very simply, props are used to pass information to components.

info

👀 "Information" can be any JS expression such as variables, functions, etc.

Basic Syntax

The syntax of passing props to a child component is much like defining attributes in an HTML element, for example, we can pass a simple string to a component like this:

<Person firstName="Fred" />

In the above example, the name of the prop is firstName.

Because JSX is transpiled into pure JS, we use lowerCamelCasing to name props. This is contrary to the convention of kebob-casing used to name attributes in HTML.

Like HTML attributes, there should be no spaces before or after the = sign.

There is no limit to the number of props that can be passed:

<Person
firstName="Fred"
age={21}
/>
caution

Any JS expression can be passed, however, expressions other than simple strings (excludes template literals), are passed within curly braces. Thus, the age prop will have the value set to the number 21.

Each prop becomes a property on a "props" object that's passed to the child component.

The next example shows how you can pass an object literal and even a function:

function handleRaise(name, amount) {
// Give the person a raise!
alert(`${name.first} raised to ${amount}`);
}

<Person
name={{first: "Fred", last: "Arroyo"}}
age={21}
handleRaise={handleRaise}
/>

Don't let those double-curly braces ({{ ... }}) confuse you. The outer curly braces simply say, "here comes a JS expression"; and the inner curly braces represent an object literal.

How the Child Components Access Props

A props object is passed to Function Components as an argument:

Live Editor
function Person(props) {
  return (
    <div>
      <p>First: {props.name.first}</p>
      <p>Last: {props.name.last}</p>
      <p>Age: {props.age}</p>
      <button onClick={() => props.handleRaise(props.name, 9999)}>
        Give Raise!
      </button>
    </div>
  );
}

// ignore the code below -> needed for live editor
function handleRaise(name, amount) {
  // Give the person a raise!
  alert(`${name.first} raised to ${amount}`);
}

function View() {
  const name = { first: "Fred", last: "Arroyo" };
  return <Person name={name} age={21} handleRaise={handleRaise} />;
}

render(<View />);
Result
Loading...

By convention, we name the parameter props instead of tuna, mamaMia, etc.

As you can see, each passed prop becomes a property on the props object that's passed to the Function Component as an argument.

If no props are passed to a component, props will be an empty object.

caution

👀 IMPORTANT: A prop's value should never be changed. Instead, a different value should be assigned to the prop at the source (the component that "owns" the info being passed).

Destructuring the props Object

In modern JavaScript, it is common to create variables from properties on objects or elements in an array using Destructuring Assignment.

The syntax is similar to "unpacking" in Python.

Here's a simple example of using destructuring assignment to create variables for an object's properties:

Live Editor
function showCar() {
  const car = {
    make: "Tesla",
    model: "Model S",
    year: 2020,
  };

  let { make, year } = car;

  return `${make} and ${year}`; //=>  "Tesla"  2020
}
Result
Loading...

We can also use destructuring assignment to destructure parameters. Accordingly, it's common to destructure the props object passed to a Function Component.

Using destructuring, the Person component refactors to:

Live Editor
function Person({ name, age, handleRaise }) {
  return (
    <div>
      <p>First: {name.first}</p>
      <p>Last: {name.last}</p>
      <p>Age: {age}</p>
      <button onClick={() => handleRaise(name, 9999)}>Give Raise!</button>
    </div>
  );
}

// ignore the code below -> needed for live editor
function handleRaise(name, amount) {
  // Give the person a raise!
  alert(`${name} raised to ${amount}`);
}

function View() {
  const name = { first: "Fred", last: "Arroyo" };
  return <Person name={name} age={21} handleRaise={handleRaise} />;
}

render(<View />);
Result
Loading...

As you can see, the advantage is more concise code in the component's function.

Be sure to check the docs to learn more about destructuring objects and arrays!

Using Props in "React To-Do"

Currently in "React To-Do", the <ToDoList> component is rendering two hard-coded <ToDoListItem> components - this isn't very useful.

Instead, we typically would be passing actual data down via props...

Let's refactor "React To-Do" so that:

  1. App.js defines a todos array of strings representing the to-dos.
  2. Pass the todos array to the <ToDoList> component as a prop.
  3. Refactor ToDoList.jsx to accept props.
  4. Use React Developer Tools to verify the prop in <ToDoList>.
  5. Create and render a <ToDoListItem> component for each element (to-do) in the todos array.
  6. Pass each "todo" string as a prop to the <ToDoListItem>.
  7. Refactor the <ToDoListItem> component to destructure the todo prop and render it instead of the "To Do Item" placeholder text.

1. Define a todos array in App.js

We can define the array outside of or within the function...

App.jsx
// App.js
import ToDoList from "./ToDoList";

// Add the todos array
const todos = [
'Have Fun',
'Learn React',
'Learn the MERN-Stack'
];

export default function App() {
caution

👀 If we were to define the todos array within the Function Component, it would be "reset" (reassigned) every time the component rendered!

2. Pass the todos array to the <ToDoList> component as a prop

App.jsx
export default function App() {
return (
<div className="App">
<h1>React To-Do</h1>
{/* Pass todos as a prop */}
<ToDoList todos={todos} />
</div>
);
}

The name of the prop, todos, can be anything you want (it does not have to match the value being assigned).

tip

👀 Comments can be added within JSX by using the inline/multi-line syntax (/* comment */) wrapped with curly braces.

3. Refactor ToDoList.jsx to accept props

Now that <ToDoList> is being passed a prop, it will need to be able to access it.

As previously discussed, Function Components need to define a parameter that by convention is named props:

Live Editor
function ToDoList(props) {
  return (
    <ul>
      <ToDoListItem />
      <ToDoListItem />
    </ul>
  );
}

// ignore the code below -> needed for live editor
function ToDoListItem() {
  return <li>ToDoListItem</li>;
}

function View() {
  return <ToDoList />;
}

render(<View />);
Result
Loading...

4. Use React Developer Tools to verify the prop in <ToDoList>

Open React Developer Tools and verify that the array has been passed!

👉 You Do - Destructuring (1 minute)

  • Destructure the props object in the parameter list of ToDoList.jsx.
tip

Hint: The name of the prop to destructure is todos.

5. Create and render a <ToDoListItem> component for each element (the to-do string) in the todos array

Our goal is to create and render a <ToDoListItem> component for each string in the todos array.

❓ What's the best array iterator method for transforming the elements of a source array into a new array?

Array.prototype.map


Here's one approach:

Live Editor
// remember to export default
function ToDoList({ todos }) {
  // Create an array of <ToDoListItem> components
  const toDoListItems = todos.map((t) => <ToDoListItem />);
  return <ul>{toDoListItems}</ul>;
}

// ignore the code below -> needed for live editor
const todos = ["Have Fun", "Learn React", "Learn the MERN-Stack"];

function ToDoListItem() {
  return <li>ToDoListItem</li>;
}

function View() {
  return <ToDoList todos={todos} />;
}

render(<View />);
Result
Loading...

The above approach is clean and it works. However, since calling the map() method is a JS expression that results in an array, it's possible to perform the mapping within the JSX like this:

Live Editor
// remember the export default
function ToDoList({ todos }) {
  return (
    <ul>
      {todos.map((t) => (
        <ToDoListItem />
      ))}
    </ul>
  );
}

// ignore the code below -> needed for live editor
const todos = ["Have Fun", "Learn React", "Learn the MERN-Stack"];

function ToDoListItem() {
  return <li>ToDoListItem</li>;
}

function View() {
  return <ToDoList todos={todos} />;
}

render(<View />);
Result
Loading...

Both approaches are acceptable.

6. Pass each "todo" string as a prop to the <ToDoListItem>

👉 You Do - Pass a todo Prop (1 minute)

  • From <ToDoList>, pass each "todo" string as a prop named todo to each <ToDoListItem>.
tip

Hint: In the mapping, the t parameter is the "todo" string

  • Use React Developer Tools to verify:

7. Refactor the <ToDoListItem> component to destructure the todo prop and render it instead of the "To Do Item" placeholder text

👉 You Do - More Prop Destructuring (1 minute)

  • You got this!

Nice!

7. Properly Rendering Arrays of Components

Even though the array of <ToDoListItem> components is rendering fine, the Console shows the following warning:

Whenever an array of components is rendered, React wants a special key prop added to each component so that it can more efficiently track changes when re-rendering.

The value assigned to the key prop must be unique among the array of components being rendered.

Since the user might enter the same To Do string, we can resort to using the index of the iteration:

ToDoList.jsx
export default function ToDoList({ todos }) {
return (
<ul>
{todos.map((t, idx) => (
<ToDoListItem todo={t} key={idx} />
))}
</ul>
);
}

In this case, we are using the index of iteration as the key's value because we can't guarantee that the to-do strings would be unique. However, if the data, for example, had an id, or another unique property, you should assign that value to the key instead.

info

👀 The key prop is internal to React and cannot be accessed by the component it's being passed to.

8. Props for React Elements

❓ What are React Elements?


React Elements are React's built-in components that emit DOM elements into the page.


Many props provide the same purpose as their corresponding HTML attributes.

For example, if you wanted to assign an id to a <div>:

<div id="board">{/* contents of the board */}</div>
caution

For example, we can style React Elements using two props similar to their HTML counterparts:

  • className: Works like the HTML class attribute, simply named differently.
  • style: Performs styling using the HTML style attribute, but must be passed an object.

9. Intro to Styling React Elements

❓ Only React Elements can be styled, not user-defined components - why?

User-defined components do not actually find their way into the DOM, only React Elements do. For example, you would never see a <ToDoList> tag in the DOM 🙂


Let's take a look at the basics of styling in React...

Including CSS Stylesheets in a React App

CSS stylesheets can be included in a React app by simply importing them as shown in App.js:

App.js
import "./styles.css";
caution

This is only made possible by the tooling, in this case, Webpack, has been configured.

When imported as shown above, all defined CSS rules will be included "globally".

For example, let's create a ToDoListItem.css stylesheet in the "React To-Do" project:

Next, using the same syntax as used in App.js, let's import ToDoListItem.css in ToDoListItem.jsx:

ToDoListItem.jsx
// ToDoListItem.jsx

// Import the styles - making them available globally
import "./ToDoListItem.css";
info

Again, importing a stylesheet includes its styles globally - it does not matter which component module the stylesheet is imported in. Also, it does matter not how many times the same stylesheet is imported, its styles will only be bundled in the resulting global CSS once.

❓ Why bother creating stylesheets for components since we can just put all of the styles in a single stylesheet like styles.css?


It's a great way to organize CSS rules that apply only to a given component!

Whereas styles.css is a great place for general, application-wide CSS.


Styling With the className Prop

Note the outer <div> in the <App> component:

App.jsx
export default function App() {
return (
<div className="App">
<h1>React To-Do</h1>
<ToDoList todos={todos} />
</div>
);
}

The className prop is used to assign a CSS class or classes to a React Element.

className is used instead of since class because it's a reserved word in the JS language. So React uses className instead which just happens to be the same property name used on DOM elements.

If you accidentally use a prop of class instead of className, the component will still render as expected, however, a warning will appear in the Console.

Let's add some CSS rules within ToDoListItem.css:

ToDoListItem.css
.ToDoListItem {
margin: 4vmin;
padding: 2vmin;
border: 1vmin solid purple;
list-style: none;
font-size: 6vmin;
}
❓ Why are we defining a class named ToDoListItem instead of using a simpler name such as list?

To prevent name collisions between classes defined elsewhere.


Finally, let's add the ToDoListItem CSS class to the <li> React Element in ToDoListItem.jsx:

ToDoListItem
export default function ToDoListItem({ todo }) {
return (
<li className="ToDoListItem">{todo}</li>

That's better!

tip

👀 Tip: We don't have to assign a simple string to className. We can compute and assign CSS class name(s) dynamically by using any JS expression within curly braces - ternary expressions, template literals, etc.!

👉 You Do - Use Another Stylesheet (5 minutes)

Although the list is looking better, notice that the it's slightly shifted to the right. This is because HTML <ul> elements have some left padding by default.

  1. Create a new stylesheet for the <ToDoList> component following the same conventions used above.

  2. Import the new stylesheet into ToDoList.jsx.

  3. In the stylesheet, define a CSS class that sets padding-left to 0.

  4. Apply that class to the <ul> React Element.

Styling Using the style Prop

The use of the className prop is generally the go to for styling, however, in cases where individual CSS property values might change with every render, the style prop provides maximum flexibility for highly dynamic styling!

caution

The style prop in React differs from its HTML counterpart in that it must be assigned a JS object instead of a string value.

To demo its use, let's say we want to alternate the background color of each To Do based on whether its index is odd or even.

So the next thing we need to ask ourselves is:
How will each <ToDoListItem> component know if its odd or even?
Well, we're certainly going to have to pass an additional prop from its parent, <ToDoList>.

Further, we have two options, we can pass the actual index of the iteration or pass a computed prop like isOdd.

The first option, passing the index, provides a bit more flexibility because maybe we'll want to use it for other purposes, like displaying the number of the To Do.

👉 You Do - Pass the Index of the Iteration (2 minutes)

  1. Pass the index of the map iteration (idx parameter) to each <ToDoListItem> component using a prop named index.

  2. In ToDoListItem.jsx, destructure the index prop being passed to it.


Now let's use the style prop, assigning it an object literal to style the background color of the <ul>:

Live Editor
function ToDoListItem({ todo, index }) {
  return (
    <li
      className="ToDoListItem"
      style={{
        backgroundColor: index % 2 ? "lavender" : "plum",
      }}
    >
      {todo}
    </li>
  );
}

// ignore the code below -> needed for live editor
function View() {
  return <ToDoListItem todo="testing" index={2} />;
}

render(<View />);
Result
Loading...

Of course, you can use any named color, hex code, or rgba() value you wish - just be sure to quote it. Also, any numeric value assigned to a CSS property uses px as its unit by default.

Using Chrome DevTools confirms that the style prop results in a style attribute being added to the DOM elements.

Styling Using CSS Modules

CSS Modules provide an alternative to importing stylesheets the way we have done thus far.

Classes defined in CSS Modules are dedicated to the component they are imported within, thus they have the advantage of avoiding name collisions.

However, they are not as flexible because they only work with class selectors.

Read this article to learn more.

Updated Fundamentals of React Chart

Before wrapping up with the Essential Questions, let's review the following updates to the Fundamentals of React Chart...

React FundamentalSummary
......
JSX
  • A syntax extension to JS that looks like HTML and makes defining UIs more intuitive vs. pure JS.
  • JSX emits text in the page when a JS expression is surrounded with curly braces
    <div>_JS Expression_</div>.
  • JSX transpiles into a function call that returns a JS object, thus, JSX can be assigned to variables, passed as arguments to functions, etc.
  • JSX can render an array of components, however, each component needs a key prop with a unique value.
    const catList = cats.map(c => <div key={cat?.id}>{cat?.name}</div>);.
......
Props
  • Props are used to pass information (any JS expression) to a child component.
  • Function Components are passed a props object as an argument.
  • Props should never be updated - consider them immutable.
  • React Elements can be passed props that correspond to HTML attributes, e.g., id, placeholder, pattern, etc. However, some are named or used slightly differently.
Styling
  • Only React Elements such as <div>, <form>, etc. can be styled because user-defined components aren't rendered to the DOM.
  • The className prop is used to assign classes and may be a JS expression resulting in a string of class name(s).
  • The style prop is used when styling needs to be computed each time the component renders and must be assigned a JS object with keys representing the camelCased names of CSS properties.

10. ❓ Essential Questions (3 mins)

(1) True or False: JSX provides a clear, declarative approach to defining user interfaces.


True


(2) True or False: React Elements are built-in components that become actual HTML elements in the page's DOM.


True


(3) Information is passed down through the component hierarchy using _______.


Props


function ListItem({ item, index }) {
index = index + 1;
return (
<div>
{index}: {item}
</div>
);
}

(4) What is wrong with the above code?


Props such as index are immutable - we don't update them.


const colors = ["red", "green", "blue"];

function ColorList() {
return colors.map((c) => <div>{c}</div>);
}

(5) The above <ColorList> component will render as expected, however, a warning will appear in the Console - why?


Components rendered as an array need a unique key prop.


BookItem.jsx
function BookItem({ book }) {
return (
<article class="BookItem">
<div>Title - {book.title}</div>
<div>Author - {book.author}</div>
</article>
);
}

(6) The above <BookItem> component will render as expected, however, a warning will appear in the Console - why?


The class prop should be className


11. 💪 Bonus Exercise - Display the To Do Number

Prior to beginning the next React lesson, do your best to:

  1. Number each To Do sequentially - make it one-based 🙂

  2. Style each To Do so that they look something like this:

tip

Hint: One word - flexbox!

References