Table of Contents
Chapter 5. React with JSX
Real React developer does not use React.createElement
function to construct React element. JSX is the default way for developer.
5.1 React Elements as JSX
In JSX:
- An element's type is specified with a tag.
- Tag's attribute represents the properties
- Properties have 2 types: string or JS expression
- Element's children are added between opening and closing tags
- JS expression in JSX must sorrounded with curly braces
{}
(e.g. array, object)
<IngredientList ...>
as tag is element's typelist:[...]
has all children.
5.1.2 JSX Tips
JSX share similarity with HTML syntax, with few tips
Nested component
JSX allows you to add components as children of other componenets
<IngredientsList>
<Ingredient />
<Ingredient />
<Ingredient />
</IngredientsList>
className
Since class
is reserved word in JS, className
is used by React to define class
attribute
<h1 className="fancy">Baked Salmon</h1>
JavaScript expression
JavaScript expression in JSX are wrapped in curly braces {}
, so they can be evaluated and returned.
<h1>{title}</h1>
<input type="checkbox" defaultChecked={false} />
Evaluation
JS between {}
will be evaluated, which include e.g. concatenation, addition, function invokation, etc.
<h1>{"Hello" + title}</h1>
<h1>{title.toLowerCase().replace}</h1>
5.1.3 Mapping Arrays with JSX
Since JSX is JS, JSX can be directly incorporated in JS functions.
e.g. map an array to JSX elements
<ul>
{props.ingredients.map((ingredient, i) => ( // Here we specify what to be returned by using `()`
<li key="{i}">{ingredient}</li>
))}
</ul>
5.2 Babel
All JSX must be converted into createElement
calls to be interpreted by a browser. Babel is one of them
JS is an interpreted language: the browser interprets the code as text (they don't need to compile JS). However different browser support different JS standard. Babel can transpile to browser understandable code.
Easiest way of work with Babel: include a link to the Babel CDN directly in HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>React Examples</title>
</head>
<body>
<div id="root"></div>
<!-- React Library & React DOM -->
<script src="https://unpkg.com/react@16.8.6/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.8.6/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<script type="text/babel">
// JSX code here. Or link to separate JavaScript file that contains JSX.
</script>
</body>
</html>
5.3 Recipes as JSX
const data = [
{
name: "Baked Salmon",
ingredients: [
{ name: "Salmon", amount: 1, measurement: "l lb" },
{ name: "Pine Nuts", amount: 1, measurement: "cup" },
{ name: "Butter Lettuce", amount: 2, measurement: "cups" },
{ name: "Yellow Squash", amount: 1, measurement: "med" },
{ name: "Olive Oil", amount: 0.5, measurement: "cup" },
{ name: "Garlic", amount: 3, measurement: "cloves" }
],
steps: [
"Preheat the oven to 350 degrees.",
"Spread the olive oil around a glass baking dish.",
"Add the yellow squash and place in the oven for 30 mins.",
"Add the salmon, garlic, and pine nuts to the dish.",
"Bake for 15 minutes.",
"Remove from oven. Add the lettuce and serve."
]
},
{
name: "Fish Tacos",
ingredients: [
{ name: "Whitefish", amount: 1, measurement: "l lb" },
{ name: "Cheese", amount: 1, measurement: "cup" },
{ name: "Iceberg Lettuce", amount: 2, measurement: "cups" },
{ name: "Tomatoes", amount: 2, measurement: "large" },
{ name: "Tortillas", amount: 3, measurement: "med" }
],
steps: [
"Cook the fish on the grill until cooked through.",
"Place the fish on the 3 tortillas.",
"Top them with lettuce, tomatoes, and cheese."
]
}
];
// The data, an array of Recipe objects
const data = [ ... ];
// A function component for an individual Recipe
function Recipe (props) {
...
}
// A function component for the Menu of Recipes
function Menu (props) {
...
}
// A call to ReactDOM.render to render our Menu into the current DOM
ReactDOM.render(
<Menu recipes={data} title="Delicious Recipes" />,
document.getElementById("root")
);
props.recipes
ofMenu
component will take the data array
Improve Menu
componenet by takes in props
argument.
function Menu(props) {
return (
<article>
<header>
<h1>{props.title}</h1>
</header>
<div className="recipes">
{props.recipes.map((recipe, i) => (
<Recipe
key={i}
name={recipe.name}
ingredients={recipe.ingredients}
steps={recipe.steps}
/>
))}
</div>
</article>
);
}
Further improvement by desctructing the props
object
function Menu({ title, recipes }) {
return (
<article>
<header>
<h1>{title}</h1>
</header>
<div className="recipes">
{recipes.map((recipe, i) => (
<Recipe key={i} {...recipe} />
))}
</div>
</article>
);
}
Complete code for app is:
const data = [
{
name: "Baked Salmon",
ingredients: [
{ name: "Salmon", amount: 1, measurement: "l lb" },
{ name: "Pine Nuts", amount: 1, measurement: "cup" },
{ name: "Butter Lettuce", amount: 2, measurement: "cups" },
{ name: "Yellow Squash", amount: 1, measurement: "med" },
{ name: "Olive Oil", amount: 0.5, measurement: "cup" },
{ name: "Garlic", amount: 3, measurement: "cloves" }
],
steps: [
"Preheat the oven to 350 degrees.",
"Spread the olive oil around a glass baking dish.",
"Add the yellow squash and place in the oven for 30 mins.",
"Add the salmon, garlic, and pine nuts to the dish.",
"Bake for 15 minutes.",
"Remove from oven. Add the lettuce and serve."
]
},
{
name: "Fish Tacos",
ingredients: [
{ name: "Whitefish", amount: 1, measurement: "l lb" },
{ name: "Cheese", amount: 1, measurement: "cup" },
{ name: "Iceberg Lettuce", amount: 2, measurement: "cups" },
{ name: "Tomatoes", amount: 2, measurement: "large" },
{ name: "Tortillas", amount: 3, measurement: "med" }
],
steps: [
"Cook the fish on the grill until hot.",
"Place the fish on the 3 tortillas.",
"Top them with lettuce, tomatoes, and cheese."
]
}
];
function Recipe({ name, ingredients, steps }) {
return (
<section id={name.toLowerCase().replace(/ /g, "-")}>
<h1>{name}</h1>
<ul className="ingredients">
{ingredients.map((ingredient, i) => (
<li key={i}>{ingredient.name}</li>
))}
</ul>
<section className="instructions">
<h2>Cooking Instructions</h2>
{steps.map((step, i) => (
<p key={i}>{step}</p>
))}
</section>
</section>
);
}
function Menu({ title, recipes }) {
return (
<article>
<header>
<h1>{title}</h1>
</header>
<div className="recipes">
{recipes.map((recipe, i) => (
<Recipe key={i} {...recipe} />
))}
</div>
</article>
);
}
ReactDOM.render(
<Menu recipes={data} title="Delicious Recipes" />,
document.getElementById("root")
);
- Here we can see the Menu and its child elements. The data array contains two objects for recipes, and we have two Recipe elements. Each Recipe element has properties for the recipe
name
,ingredients
, andsteps
. The ingredients and steps are passed down to their own components as data.
Effect of rendered HTML
React virtual DOM of can be viewed by using React Developer Tools
5.4 React Fragments
- React won't render multiple adjacent elements as a single component, so we used to have to wrap these in an enclosing tag like a
div
(e.g. shown above inRecipe
component). - Error
Adjacent JSX elements must be wrapped in an enclosing tag
will be generate from console - Hence, a log of unnecessary tags are created (wrapper)
- Solution is using
React.Fragment
tag (a relative new feature)
e.g. Functional component return multiple adjacent/sibling components
function Cat({ name }) {
return (
<h1>The cat's name is {name}</h1>
<p>He's good.</p>
);
}
How to use <React.Fragment>
: Wrapping adjacent tags h1
and p
with React.Fragment
tag:
function Cat({ name }) {
return
<React.Fragment>
<h1>The cat's name is {name}</h1>
<p>He's good.</p>
</React.Fragment>
}
Fragment shorthand <>
simplify the wrapping:
function Cat({ name }) {
return (
<>
<h1>The cat's name is {name}</h1>
<p>He's good.</p>
</>
);
}
After rendering, DOM is shown below (fragment is invisible)
<div id="root">
<h1>The cat's name is Jungle</h1>
<p>He's good</p>
</div>
5.5 Intro to webpack
Webpack is a module bundler that takes all different files (JS, LESS, CSS, JSX, ESNext, etc) to turn them into a single file.
Advantage of module bundler:
- modularity
- network performance
Aside from code compilation, webpack can handle:
- Code splitting
- Minification
- Feature Flagging
- Hot Module Replacement (HMR)
Benefit of incorporating webpack module bundler:
- Modularity
- Composition
- Speed
- Consistency
5.5.1 Creating the Project
5.5.2 Loading the Bundle
5.5.3 Source Mapping
- Problem: Bunding the code into a single file makes debugging difficult in browser, as developer tool shows the source code which is not human friendly.
- Solution: Source Map is a file that maps a bundle to the original source file.
- How to use create/use source map: add
devtool
property to#source-map
inwebpack.config.js
, which tell webpack we want to use source mapping. (as shown below)dist
folder will havebundle.js
andbundle.js.map
..map
file let you debug using original source file.- In Source tab of browser's developer tools, we can find
webpack://
folder containing all files used to debug
// webpack.config.js with source mapping
module.exports = {
...
devtool: "#source-map"
}