Chapter 4. How React Works
4.1 Page Setup
For browser to work with React, browser need to load at least 2 libraries React & ReactDOM (as shown below)
Simplest form of HTML for working with React
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>React Samples</title>
</head>
<body>
<!-- Target container -->
<div id="root"></div>
<!-- React library & ReactDOM (Development Version)-->
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script>
// Pure React and JavaScript code
</script>
</body>
</html>
4.2 React Elements
A React element is a description of what the actual HTML DOM element should look like.
- React element are instructions for how the browser should be created.
e.g. create a React element to represent an h1
using React.createElement
React.createElement("h1", { id: "recipe-0" }, "Baked Salmon");
Where:
- 1st arg defines type of DOM element to create
- 2nd arg is properties of element
- 3rd/4th/following arg is children of this React element, can be a single text or other React element.
React element is converted into DOM element during rendering as shown below:
<h1 id="recipe-0">Baked Salmon</h1>
The react element returned from .createElement
is a JS literal that tells React how to construct DOM element. If logged, it's as follow:
{
$$typeof: Symbol(React.element),
"type": "h1",
"key": null,
"ref": null,
"props": {id: "recipe-0", children: "Baked Salmon"},
"_owner": null,
"_store": {}
}
Where:
- properties (i.e. content of
"props"
exceptchildren
) are added to the HTML tag<...>
as attribute - child text is added as text within the element
- structure of React element has fields:
$$typeof
,type
,key
,ref
,props
,_owner
,_store
.type
property of React element tells React what type of HTML or SVG element to createprops
property is data and child elements required to construct a DOM elementchildren
property is for displaying other nested elements as text
4.3 React DOM
- After React element (JS literal) created, ReactDOM render React element in browser.
- ReactDOM has
render
method. (shown below)ReactDOM.render(...)
has 2 args:- 1st arg is React Element we want to render.
- 2nd arg is target node to be rendered.
- ReactDOM has all tools needed for rendering elements.
const dish = React.createElement("h1", null, "Baked Salmon"); // create a text element as child of `h1` element
ReactDOM.render(dish, document.getElementById("root")); // Render titlement element (h1) to DOM, hence add `h1` element to `div` with `id` of `root`
resulting following HTML
<body>
<div id="root">
<h1>Baked Salmon</h1>
</div>
</body>
.render
can render array since React16
const dish = React.createElement("h1", null, "Baked Salmon");
const dessert = React.createElement("h2", null, "Coconut Cream Pie");
ReactDOM.render([dish, dessert], document.getElementById("root"));
4.3.1 Children
Objective: understand how to use props.children
Given a HTML DOM tree (unordered list) as shown below
<ul>
<li>2 lb salmon</li>
<li>5 sprigs fresh rosemary</li>
<li>2 tablespoons olive oil</li>
<li>2 small lemons</li>
<li>1 teaspoon kosher salt</li>
<li>4 cloves of chopped garlic</li>
</ul>
We can create React element using following:
const list = React.createElement(
"ul",
null,
React.createElement("li", null, "2 lb salmon"),
React.createElement("li", null, "5 sprigs fresh rosemary"),
React.createElement("li", null, "2 tablespoons olive oil"),
React.createElement("li", null, "2 small lemons"),
React.createElement("li", null, "1 teaspoon kosher salt"),
React.createElement("li", null, "4 cloves of chopped garlic")
);
The React element JS literal is as follow:
console.log(list);
{
"type": "ul",
"props": {
"children": [
{ "type": "li", "props": { "children": "2 lb salmon" } … },
{ "type": "li", "props": { "children": "5 sprigs fresh rosemary"} … },
{ "type": "li", "props": { "children": "2 tablespoons olive oil" } … },
{ "type": "li", "props": { "children": "2 small lemons"} … },
{ "type": "li", "props": { "children": "1 teaspoon kosher salt"} … },
{ "type": "li", "props": { "children": "4 cloves of chopped garlic"} … }
]
...
}
}
Where:
props.children
is an element tree- Each list item is a child.
For more complex recipe to like HTML shown
<section id="baked-salmon">
<h1>Baked Salmon</h1>
<ul class="ingredients">
<li>2 lb salmon</li>
<li>5 sprigs fresh rosemary</li>
<li>2 tablespoons olive oil</li>
<li>2 small lemons</li>
<li>1 teaspoon kosher salt</li>
<li>4 cloves of chopped garlic</li>
</ul>
<section class="instructions">
<h2>Cooking Instructions</h2>
<p>Preheat the oven to 375 degrees.</p>
<p>Lightly coat aluminum foil with oil.</p>
<p>Place salmon on foil</p>
<p>Cover with rosemary, sliced lemons, chopped garlic.</p>
<p>Bake for 15-20 minutes until cooked through.</p>
<p>Remove from oven.</p>
</section>
</section>
We can create use React element via
React.createElement(
"section", // type of DOM element
{ id: "baked-salmon " }, // properties (e.g. props) of element
React.createElement("h1", null, "Baked Salmon"), // First child
React.createElement( // Second child (i.e. the ul with contents)
"ul",
{ className: "ingredients" },
React.createElement("li", null, "2 lb salmon"),
React.createElement("li", null, "5 springs fresh rosemary"),
React.createElement("li", null, "2 tablespoons olive oil"),
React.createElement("li", null, "2 small lemons"),
React.createElement("li", null, "1 teaspoon kosher salt"),
React.createElement("li", null, "4 cloves of chopped garlic")
),
React.createElement( // Third child (i.e. section with content)
"section",
{ className: "instructions" },
React.createElement("h2", null, "Cooking Instructions"),
React.createElement("p", null, "Preheat the oven to 375 degress."),
React.createElement("p", null, "Lightly cost aluminum foil with oil."),
React.createElement("p", null, "Place salmon on foil"),
React.createElement("p", null, "Cover with rosemary, sliced lemons, chopped garlic."),
React.createElement("p", null, "Bake for 15-20 minutes until cooked through."),
React.createElement("p", null, "Remove from oven.")
)
);
Where:
- React use
className
to define theclass
attribute of an HTML element.
Constructing elements with data
React can let us to separate data from UI (e.g. data in array, while map array to React element using JS logic)
e.g. Store recipe data in array, then map it to ReactDOM of list
// Store recipe data in array
const items = [
"2 lb salmon",
"5 sprigs fresh rosemary",
"2 tablespoons olive oil",
"2 small lemons",
"1 teaspoon kosher salt",
"4 cloves of chopped garlic"
]
React.createElement(
"ul",
{ className: "ingredient" },
items.map((ingredient, i) =>
React.createElement("li", { key: i }, ingredient)
)
)
Where:
- key property of React elements: React like each element to have a
key
property, which is used by React to update its DOM efficiently. Without this property, warning errorWarning: Each child in an array or iterator should have a unique "key" prop. ...
will show up. - Here we use array index (Check the syntax of
Array.map
) as unique key value of each ingredient (React element).
4.4 React Components
Component: parts used by react to construct UI (e.g. Buttons, List, Heading, etc.)
- React component allow us to reuse same structure by fill them with different datas.
- How to create React component: write a function that return a component (i.e. Functional Component, a good pattern).
e.g. Handwritten React element
function IngredientsList() {
return React.createElement(
"ul",
{ className: "ingredients" },
React.createElement("li", null, "1 cup unsalted butter"),
React.createElement("li", null, "1 cup crunchy peanut butter"),
React.createElement("li", null, "1 cup brown sugar"),
React.createElement("li", null, "1 cup white sugar"),
React.createElement("li", null, "2 eggs"),
React.createElement("li", null, "2.5 cups all purpose flour"),
React.createElement("li", null, "1 teaspoon baking powder"),
React.createElement("li", null, "0.5 teaspoon salt")
);
}
ReactDOM.render(
React.createElement(IngredientsList, null, null),
document.getElementById("root")
)
It generate following HTML DOM
<IngredientsList>
<ul className="ingredients">
<li>1 cup unsalted butter</li>
<li>1 cup crunchy peanut butter</li>
<li>1 cup brown sugar</li>
<li>1 cup white sugar</li>
<li>2 eggs</li>
<li>2.5 cups all purpose flour</li>
<li>1 teaspoon baking powder</li>
<li>0.5 teaspoon salt</li>
</ul>
</IngredientsList>
We can achieve above by: 1. create a component to be reused; 2. parse data
// Define data
const secretIngredients = [
"1 cup unsalted butter",
"1 cup crunchy peanut butter",
"1 cup brown sugar",
"1 cup white sugar",
"2 eggs",
"2.5 cups all purpose flour",
"1 teaspoon baking powder",
"0.5 teaspoon salt"
];
// Define component
function IngredientsList() {
return React.createElement(
"ul",
{ className: "ingredients" },
items.map((ingredient, i) =>
React.createElement("li", { key: i }, ingredient)
)
);
}
// Render
ReactDOM.render(
React.createElement(
IngredientsList, { items: secretIngredients }, null),
document.getElementById("root")
);
It rendered to following HTML DOM
<IngredientsList items="[...]">
<ul className="ingredients">
<li key="0">1 cup unsalted butter</li>
<li key="1">1 cup crunchy peanut butter</li>
<li key="2">1 cup brown sugar</li>
<li key="3">1 cup white sugar</li>
<li key="4">2 eggs</li>
<li key="5">2.5 cups all purpose flour</li>
<li key="6">1 teaspoon baking powder</li>
<li key="7">0.5 teaspoon salt</li>
</ul>
</IngredientsList>