Copyright Derek O'Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.
React is only concerned with front-end rendering. React can be used as the View in an MVC framework. React uses one-way data flow, which helps developers to write modular code that is easy to maintain.
In order to use React on the client-side, we need to include the two Javascript files below:
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
React is object oriented. Two main classes in the React library are:
In order to use React, we only need to use one method from each of the two classes:
The React.createElement() method creates and returns a new React element of a given type. The type argument is a string containing an element's type, such as "div" or "form".
React.createElement() method takes three arguments:
For example:
React.createElement("div", {name:"helloDiv", style: {color: "red", border:"thin solid #aaa"}}, "Hello World")
The ReactDom.render() method accepts two arguments. The first argument is the element that will be rendered. The element is created using the React.createElement() method, as described above. The second argument states where the element will render.
For example:
ReactDOM.render(React.createElement("p",null,"Hello World"), document.getElementById("root"))
JSX is a preprocessor that makes it easier to work with the React.createElement() method. JSX elements have a name (e.g. "div", "h1"), attributes (e.g. name="user_password", type="password") and children (e.g. <input> elements inside a <form> element).
In order to use JSX, we need to include the JSX library and we need to set the script type to "text/babel".
JSX is not a string. It is not enclosed in quotes.
JSX expressions can be assigned to variables.
An attribute can either be a string or a JavaScript expression. If an attribute value is enclosed in quotes, the value is a string. If an attribute value is enclosed in {} curly brackets, the value is a JavaScript expression. We cannot put quotes around {} curly brackets when embedding a JavaScript expression in an attribute.
The code below highlights the points made above.
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> ... ... <script type="text/babel"> let defaultName = "DkIT" let simpleForm = <form><input type="text" name="college" value={defaultName}/></form> ReactDOM.render(simpleForm, document.getElementById("simpleFormcontainer"))
In order to use JSX, we need to include the babel javascript library.
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
We also need to set the javascript type to "text/babel".
<script type="text/babel">
JSX is not a string. It is not enclosed in quotes.
let simpleForm = <form><input type="text" name="college" value={defaultName}/></form>
JSX expressions can be assigned to variables. In this example, the JSX has been assigned to the variable simpleForm;
let simpleForm = <form><input type="text" name="college" value={defaultName}/></form>
An attribute can either be a string or a JavaScript expression. If an attribute value is enclosed in quotes, the value is a string. If an attribute value is enclosed in {} curly brackets, the value is a JavaScript expression. Do not put quotes around {} curly brackets when embedding a JavaScript expression in an attribute.
The variable defaultName is an expression, so it is assigned its element attribute using {} brackets.
let defaultName = "DkIT" let simpleForm = <form><input type="text" name="college" value={defaultName}/></form>
The last line of code is used to render simpleForm.
ReactDOM.render(simpleForm, document.getElementById("simpleFormcontainer"))
JSX allow us to include line breaks, in the same way that literal templates do in javascript.
// without linebreaks let simpleForm = <form><input type="text" name="college" value={defaultName}/></form> // using linebreaks makes the code more readable let simpleForm = <form> <input type="text" name="college" value={defaultName} /> </form>
JSX is much more intuitive to code and debug than React.createElement(). The two segments of code below produce the same output. It is easier to understand the JSX version of the code.
// Using the React.createElement() method <script> ReactDOM.render(React.createElement('form',{id:"loginForm"}, [ React.createElement('input', {type:"email", name:"email", placeholder:"Email"}, null), React.createElement('input', {type:"password", name:"password", placeholder:"Password"}, null), React.createElement('input', {type:"submit", value:"Login"}, null) ]), document.getElementById("container") ) </script> // Using JSX <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> ... ... <script type="text/babel"> ReactDOM.render(<form id="loginForm"> <input type = "email" name = "email" placeholder = "Email" /> <input type = "password" name = "password" placeholder = "Password" /> <input type = "submit" name = "Login" /> </form>, document.getElementById("container")) </script>
The full code example of the above code snippet using React.createElement() (Run Example)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>React createElement() example</title>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<style>
{
width:300px;
border:thin solid #aaaaaa;
padding: 20px;
margin-left: auto;
margin-right: auto;
border-radius: 5px;
background: #f7f7f7;
}
input
{
display: block;
width: 100%;
padding: 5px 10px;
margin: 10px 0;
border-radius: 5px;
border: 1px solid #ddd;
}
input
{
box-sizing: border-box;
}
input:focus
{
border-radius: 0px;
border-color:blue;
}
input[type=submit]
{
display: block;
width: 100%;
border-radius: 5px;
background-color: #84c00c;
color: #fff;
border: none;
font-size: 16px;
height: 40px;
margin-top: 30px;
}
input[type=submit]:hover
{
background-color: #669509;
}
input[type=submit]:disabled
{
background-color: #dbf99f;
color: #fcc;
}
</style>
</head>
<body>
<h2>Example using React.createElement() instead of JSX</h2>
<div id="container"></div>
<!-- This code needs to be at the bottom of the BODY element -->
<script>
ReactDOM.render(React.createElement('form',{id:"loginForm"},
[ React.createElement('input', {type:"email", name:"email", placeholder:"Email"}, null),
React.createElement('input', {type:"password", name:"password", placeholder:"Password"}, null),
React.createElement('input', {type:"submit", value:"Login"}, null)
]),
document.getElementById("container")
)
</script>
</body>
</html>
The same example using JSX instead of React.createElement() (Run Example)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>React createElement() example</title> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <style> { width:300px; border:thin solid #aaaaaa; padding: 20px; margin-left: auto; margin-right: auto; border-radius: 5px; background: #f7f7f7; } input { display: block; width: 100%; padding: 5px 10px; margin: 10px 0; border-radius: 5px; border: 1px solid #ddd; } input { box-sizing: border-box; } input:focus { border-radius: 0px; border-color:blue; } input[type=submit] { display: block; width: 100%; border-radius: 5px; background-color: #84c00c; color: #fff; border: none; font-size: 16px; height: 40px; margin-top: 30px; } input[type=submit]:hover { background-color: #669509; } input[type=submit]:disabled { background-color: #dbf99f; color: #fcc; } </style> </head> <body> <h2>Example using JSX instead of React.createElement()</h2> <div id="container"></div> <!-- This code needs to be at the bottom of the BODY element --> <script type="text/babel"> ReactDOM.render(<form id="loginForm"> <input type = "email" name = "email" placeholder = "Email" /> <input type = "password" name = "password" placeholder = "Password" /> <input type = "submit" name = "Login" /> </form>, document.getElementById("container")) </script> </body> </html>
Three important differences in the way that attributes are named in HTML and JSX are:
// HTML <div class="simple" style="text-align:center; color:red">Hello World</div> // JSX <div className="simple" style={{textAlign:"center", color:"red"}}>Hello World</div> // Even if there is only one style, the double {{..}} brackets need to be used <div className="simple" style={{textAlign:"center"}}>Hello World</div>
We can run React without any server-side code by including the three js files highlighted in red below and setting the React scripts to "text/babel".
<!DOCTYPE HTML> <html> <head> <title>React Example</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Language" content="en" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <!-- When including external js files, remember to make the type="text/babel", as shown below --> <!-- <script type="text/babel" src="myExternalFile.js"></script> --> <script type="text/babel"> "use strict" let contentNode = document.getElementById("root") let component = <p>This React component is rendering without any server-side code.</p> // A simple JSX component </script> </head> <body> <div id="root"></div> <!-- This code needs to be at the bottom of the BODY element --> <script type="text/babel"> ReactDOM.render(component, contentNode) </script> </body> </html>
The three lines of code below link to the javascript files that are needed to run React from the client-side.
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
React uses HTML-like code, called "babel", as its mark-up language. We must set this as the script type, as shown below.
<script type="text/babel"> "use strict" ... </script>
In React, we use strict mode. With strict mode, we cannot use undeclared variables.
<script type="text/babel"> "use strict" ... </script>
React is ideally suited to creating both simple and highly interactive forms that combine HTML structure and javascript error-checking code in the same class.
Although it is possible to create components for individual React elements, it is usually best to treat a complete form as a single component. This is because, within forms, validation is not always at the element level. For example, in a registration form, the "confirm password" input should match the "password" input.
When creating React forms, you should pass all of the validation over to the React form. You should not perform any HTML validation, such as patterns or HTML5 automatic validation for email, url, search, number, date, et cetera. This means that you should always set the form's novalidate attribute to true.
Example of a Login Form that has all validation done within the React form component (Run Example)
<!DOCTYPE html>
<html>
<head>
<title>Login form with novalidate set example</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta http-equiv="Content-Language" content="en" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- When including external js files, remember to make the type="text/babel", as shown below -->
<!-- <script type="text/babel" src="myExternalFile.js"></script> -->
<style>
{
width:300px;
border:thin solid #aaaaaa;
padding: 20px;
margin-left: auto;
margin-right: auto;
border-radius: 5px;
background: #f7f7f7;
}
input
{
display: block;
width: 100%;
padding: 5px 10px;
margin: 10px 0;
border-radius: 5px;
border: 1px solid #ddd;
}
div.error
{
padding:5px;
background-color:#fbb;
border:thin solid #aaa;
border-radius:5px;
}
input
{
box-sizing: border-box;
}
input:focus
{
border-radius: 0px;
border-color:blue;
}
input[type=submit]
{
display: block;
width: 100%;
border-radius: 5px;
background-color: #84c00c;
color: #fff;
border: none;
font-size: 16px;
height: 40px;
margin-top: 30px;
}
input[type=submit]:hover
{
background-color: #669509;
}
</style>
<script type="text/babel">
"use strict"
class LoginForm extends React.Component
{
constructor()
{
super()
this.state =
{
email: "",
password: "",
wasSubmittedAtLeastOnce: false
};
}
handleEmailChange = e =>
{
this.setState({email: e.target.value})
}
handlePasswordChange = e =>
{
this.setState({password: e.target.value})
}
handleSubmit = e =>
{
this.setState({wasSubmittedAtLeastOnce: true})
const formInputsState = this.validate()
if (Object.keys(formInputsState).every(key => formInputsState[key]))
{
alert(`Registered details to be submitted are... email: ${this.state.email} password: ${this.state.password}`);
}
else // invalid inputs in form
{
e.preventDefault()
return
}
}
validateEmail()
{
// valid email pattern
const pattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
return pattern.test(String(this.state.email).toLowerCase())
}
validatePassword()
{
const pattern = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[£!#€$%^&*]).{10,}$/
return pattern.test(String(this.state.password))
}
validate()
{
return {
email: this.validateEmail(),
password: this.validatePassword()
}
}
render()
{
let errorMessage = ""
if(this.state.wasSubmittedAtLeastOnce)
{
errorMessage = <div className="error">Login Details are incorrect<br/></div>
}
return (
<form noValidate = {true} id = "loginForm" onSubmit = {this.handleSubmit}>
{errorMessage}
<input
name = "email"
type = "email"
placeholder = "Email"
value = {this.state.email}
onChange = {this.handleEmailChange}
/>
<input
name = "password"
type = "password"
placeholder = "Password"
value = {this.state.password}
onChange = {this.handlePasswordChange}
/>
<input
type = "submit"
value = "Login"
/>
</form>
)
}
}
</script>
</head>
<body>
<h2>Login form example</h2>
<p>In this example, the form will not submit unless the email is a valid email and the password is at least ten-digits long and contains at least one lowercase letter, one uppercase letter, one digit and one of the following characters (£!#€$%^&*)</p>
<div id="container"></div>
<!-- This code needs to be at the bottom of the BODY element -->
<script type="text/babel">
ReactDOM.render(<LoginForm />, document.getElementById("container"))
</script>
</body>
</html>
If the input element name attribute matches the state name for each input (which should always be the case), then we can replace the handleEmailChange and handlePasswordChange methods with a single handleChange method.
... this.state = { email: "", password: "", wasSubmittedAtLeastOnce:false } handleChange = e => { this.setState({[e.target.name]: e.target.value}) } ... ... <input name = "email" type = "email" placeholder = "Email" value = {this.state.email} onChange = {this.handleChange} /> <input name = "password" type = "password" placeholder = "Password" value = {this.state.password} onChange = {this.handleChange} /> ...
Adjust the Login Form from the example above with a single handleChange method, as shown here.
The componentDidMount() method is called once. It is called immediately after the first time that the component's render() method has been called. It signals that the component and all its sub-components have rendered properly.
componentDidMount is a good place to do setup that requires the Virtual DOM to be already loaded. For example:
In order to focus() an element, we first need to set a refererence to that element. This is done by adding the code below to the element's element, as shown below:
<input
name = "email"
className = {formInputsState.email ? null : "error"}
type = "text"
placeholder = "Email"
value = {this.state.email}
onChange = {this.handleChange}
ref = {input => this.inputToFocus = input}
/>
The referenced element is then focused when the React component first renders. This is done in the componentDidMount() method, as shown below:
componentDidMount()
{
this.inputToFocus.focus()
}
Example of a registration form with its "email" element focused (Run Example)
<!DOCTYPE html> <html> <head> <title>Login form example</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Language" content="en" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <!-- When including external js files, remember to make the type="text/babel", as shown below --> <!-- <script type="text/babel" src="myExternalFile.js"></script> --> <style> { width:300px; border:thin solid #aaaaaa; padding: 20px; margin-left: auto; margin-right: auto; border-radius: 5px; background: #f7f7f7; } input { display: block; width: 100%; padding: 5px 10px; margin: 10px 0; border-radius: 5px; border: 1px solid #ddd; } div.error { padding:5px; background-color:#fbb; border:thin solid #aaa; border-radius:5px; } input { box-sizing: border-box; } input:focus { border-radius: 0px; border-color:blue; } input[type=submit] { display: block; width: 100%; border-radius: 5px; background-color: #84c00c; color: #fff; border: none; font-size: 16px; height: 40px; margin-top: 30px; } input[type=submit]:hover { background-color: #669509; } </style> <script type="text/babel"> "use strict" class LoginForm extends React.Component { constructor() { super() this.state = { email:"", password:"", wasSubmittedAtLeastOnce:false } } componentDidMount() { this.inputToFocus.focus() } handleChange = e => { this.setState({[e.target.name]: e.target.value}) } handleSubmit = e => { this.setState({ wasSubmittedAtLeastOnce: true }) const formInputsState = this.validate() if (Object.keys(formInputsState).every(key => formInputsState[key])) { alert(`Registered details to be submitted are... email: ${this.state.email} password: ${this.state.password}`) } else // invalid inputs in form { e.preventDefault() return; } }; validateEmail() { // valid email pattern const pattern = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ return pattern.test(String(this.state.email).toLowerCase()) } validatePassword() { const pattern = /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[£!#€$%^&*]).{10,}$/ return pattern.test(String(this.state.password)) } validate() { return { email: this.validateEmail(), password: this.validatePassword() } } render() { let errorMessage = "" if(this.state.wasSubmittedAtLeastOnce) { errorMessage = <div className="error">Login Details are incorrect<br/></div> } return ( <form noValidate id="loginForm" onSubmit = {this.handleSubmit}> {errorMessage} <input name = "email" type = "text" placeholder = "Email" value = {this.state.email} onChange = {this.handleChange} ref = {input => this.inputToFocus = input} /> <input name = "password" type = "password" placeholder = "Password" value = {this.state.password} onChange = {this.handleChange} /> <input type = "submit" value = "Login" /> </form> ) } } </script> </head> <body> <h2>Login form example with generic handleChange method</h2> <p>In this example, the form will not submit unless the email is a valid email and the password is at least ten-digits long and contains at least one lowercase letter, one uppercase letter, one digit and one of the following characters (£!#€$%^&*)</p> <div id="container"></div> <!-- This code needs to be at the bottom of the BODY element --> <script type="text/babel"> ReactDOM.render(<LoginForm />, document.getElementById("container")) </script> </body> </html>
Adjust the code above to make a registration form, as shown here.
Write registration form code that disables the registration button until all inputs are valid and highlights invalid inputs with a red border, as shown here. As with the Login example above, the password must be at least ten-digits long and it must contain at least one lowercase letter, one uppercase letter, one digit and one of the following characters (£!#€$%^&*),
Adjust the code from the previous answer to give an error message for each input, as shown here.
Adjust the code to show a ✔ mark, if the input matches the required input, as shown here.
React can also be used to render non-interactive data, such as lists, tables and groups of DIVs. These are called 'dumb' components or ‘presentational’ components. As they do not contain any inputs that change, they only need to have a render() method. They do not need to have a super() method or any event-related methods.
Example of a React List, which only contains a render() method (Run Example)
<!DOCTYPE html> <html> <head> <title>List example</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Language" content="en" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <!-- When including external js files, remember to make the type="text/babel", as shown below --> <!-- <script type="text/babel" src="myExternalFile.js"></script> --> <script type="text/babel"> "use strict" class NameList extends React.Component { render() { let names = ["Alice", "Brian", "Colm", "Deirdre"] return ( <div id="namesList"> <ul>{names.map(name => <li>{name}</li>)}</ul> </div> ) } } </script> </head> <body> <h2>List example</h2> <div id="container"></div> <!-- This code needs to be at the bottom of the BODY element --> <script type="text/babel"> ReactDOM.render(<NameList />, document.getElementById("container")) </script> </body> </html>
We can place the code for a list into a variable. This can make our code easier to understand.
Example where the list is converted into a variable (Run Example)
<!DOCTYPE html> <html> <head> <title>List example</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Language" content="en" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <!-- When including external js files, remember to make the type="text/babel", as shown below --> <!-- <script type="text/babel" src="myExternalFile.js"></script> --> <script type="text/babel"> "use strict" class NameList extends React.Component { render() { let names = ["Alice", "Brian", "Colm", "Deirdre"] let namesList = <ul>{names.map(name => <li>{name}</li>)}</ul> return ( <div id="namesList"> {namesList} </div> ) } } </script> </head> <body> <h2>List example</h2> <div id="container"></div> <!-- This code needs to be at the bottom of the BODY element --> <script type="text/babel"> ReactDOM.render(<NameList />, document.getElementById("container")) </script> </body> </html>
Write code to show a registration form that includes a list of error messages, as shown here.
Check the browser inspector (F12) for the code that you wrote for the above question. It will give a warning that relates to the list items not having unique keys.
Keys are used by React to identify which items in a collection of items (such as lists, table rows and groups of DIVs) have changed, have been added, or have been removed. React only re-renders elements whose content has changed for a specific key. This makes rendering much more efficient. A key must uniquely identify an item among its siblings. If no key is given, then the default key is set to the element's index within the collection of items. We should only use a default index if the collection of items is never going to change and the collection of items is never going to be reordered or filtered.
When using JSON objects, we would normally have an ID as part of each object. This ID is ideal to use as a key.
Example of a JSON object with an ID being used as a list key (Run Example)
<!DOCTYPE html>
<html>
<head>
<title>JSON example</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<meta http-equiv="Content-Language" content="en" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". -->
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- When including external js files, remember to make the type="text/babel", as shown below -->
<!-- <script type="text/babel" src="myExternalFile.js"></script> -->
<style>
#countriesList
{
width:300px;
border:thin solid #aaaaaa;
padding: 20px;
margin-left: auto;
margin-right: auto;
border-radius: 5px;
background: #f7f7f7;
}
#countriesList ul
{
padding-left:0px;
list-style-type:none;
}
</style>
<script type="text/babel">
"use strict"
class JSONForm extends React.Component
{
render()
{
let countries = [{id:1, name:"Spain"}, {id:2,name: "France"}, {id:3, name:"Germany"}]
let countriesList = <ul>{countries.map(country => <li key={country.id}> {country.name} </li>)}</ul>
return (
<div id="countriesList">
{countriesList}
</div>
)
}
}
</script>
</head>
<body>
<h2>JSON key example</h2>
<div id="container"></div>
<!-- This code needs to be at the bottom of the BODY element -->
<script type="text/babel">
ReactDOM.render(<JSONForm />, document.getElementById("container"))
</script>
</body>
</html>
Check the browser code inspector (F12). It no longer gives the warning relating to unique keys .
Write code to show a list of employee details, as shown here. Use the JSON object below:
[{id:1, forename:"Ann", surname:"Anglesey", role:"IT"},
{id:2, forename:"Brian", surname:"Brown", role:"HR"},
{id:3, forename:"Cathy", surname:"Connolly",role:"HR"},
{id:4, forename:"Dennis", surname:"Deagan", role:"IT"},
{id:5, forename:"Emma", surname:"Epstein", role:"HR"} ]
We can add keys to DIVs. This would make sense if we intent to modify, add or delete to a set of DIVs. Write code to display information about a JSON set of countries, as shown here. Display the output in a set of DIVs and assign a key to each DIV.
Write code to show a registration form that includes a list of error messages, as shown here.
Write code to show a registration form that includes a list of error messages and a ✔ mark, if the input matches the required input, as shown here.
Props (properties) allow us to pass data into a React component.
Props are passed to React components via HTML attributes.
Props are read-only.
Example passing an array of names to a React component (Run Example)
<!DOCTYPE html> <html> <head> <title>Props example</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Language" content="en" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <!-- When including external js files, remember to make the type="text/babel", as shown below --> <!-- <script type="text/babel" src="myExternalFile.js"></script> --> <script type="text/babel"> "use strict" class NameList extends React.Component { constructor(props) { super(props) } render() { return ( <ul> {this.props.names.map(name => <li key={name}> {name} </li>)} </ul> ); } } </script> </head> <body> <h2>Props list example</h2> <div id="container"></div> <!-- This code needs to be at the bottom of the BODY element --> <script type="text/babel"> ReactDOM.render(<NameList names = {["Alice", "Brian", "Colm", "Deirdre"]}/>, document.getElementById("container")) </script> </body> </html>
It can sometimes make sense to create a separate React component to hold an individual output, such as list items of a list or rows of a table.
Example of a list being put into its own component (Run Example)
<!DOCTYPE html> <html> <head> <title>Multiple Components example</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Language" content="en" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <!-- When including external js files, remember to make the type="text/babel", as shown below --> <!-- <script type="text/babel" src="myExternalFile.js"></script> --> <script type="text/babel"> "use strict" class PeopleItem extends React.Component { constructor(props) { super(props) } render() { return ( <li key={this.props.key}>{this.props.name}</li> ) } } class People extends React.Component { constructor(props) { super(props) } render() { return ( <ul> {this.props.names.map(name => <PeopleItem key={name} name={name}/>)} </ul> ) } } </script> </head> <body> <h2>Multiple Components example</h2> <div id="container"></div> <!-- This code needs to be at the bottom of the BODY element --> <script type="text/babel"> ReactDOM.render(<People names = {["Alice", "Brian", "Colm", "Deirdre"]}/>, document.getElementById("container")) </script> </body> </html>
Write code to display a table that holds the country, capital city, region, and population of the world's countries, as shown here. You must put the table into its own component class. Use the dataset from this link, which is based on the dataset from https://restcountries.com/v3.1/all). Assign the dataset to a variable in your code. Do not try to read it from a JSON file, as this will not work (as it requires the getDerivedStateFromProps() method that we have not yet covered).
By default, the constructor for a component will only be called once. Sometimes, we want to send different props to the component each time a given component is rendered. We use the getDerivedStateFromProps() method to do this. If it is present, the getDerivedStateFromProps() method is automatically called immediately before the render() method.
The getDerivedStateFromProps() method should only be used when the state depends on changes in props over time. The getDerivedStateFromProps() method returns an object that is used to update the state. It should return null if the state does not need to be updated. As the getDerivedStateFromProps() method is called before every render, we should add code to only update the state when there has been a change in the props, as shown below:
static getDerivedStateFromProps(props, state) { return(state.someItem !== props.someItem ? {someItem: props.someItem} : null) }
From the example above, place the countries data into an external JSON file, as shown here. Hint: Use ComponentDidMount() in the CountriesForm component to fetch the data and use getDerivedStateFromProps() in the CountriesTable component to update the props when the JSON data has been read.
Write code to allow a user to filter the countries' JSON data, as shown here. Do not put the drop-down regions list into its own component class.
In the exercise above we can move the drop-down regions list into its own component. This will result in the drop-down regions list and the table component both being children of the main component. If we do this, we run into the issue of how the we get the event of clicking the regions list to cause the table component to update. The solution is to keep the event handler for the drop-down list in the main component. The function is passed as a property to the drop-down list component.
class DropDownRegionsList extends React.Component { ... render()
{
return (
<select onChange={this.props.handleRegionsChange}>
...
</select>
)
} } class CountriesForm extends React.Component { ... handleRegionsChange = e => { // This code will run when handleRegionsChange is called in DropDownRegionsList ... } render() { return ( <div> <DropDownRegionsList handleRegionsChange={this.handleRegionsChange}/> </div> ) } }
Write code to move the drop-down regions list to its own component, as shown here.
Adjust the code from the previous question to allow a user to click the column headers of a table to sort the rows of the table, as shown here. Whenever the user changes region, the table should be sorted in ascending order by country name.
React can use type-checking to validate a component's props when a React component loads.
Some common PropTypes are:
The full set of propTypes can be found at this link.
To do type-checking validation we set the propTypes object for any prop that we wish to validate, as shown below:
static propTypes =
{
name: PropTypes.string,
age: PropTypes.number
}
propTypes is made static, because it is shared across all instances of the object. Making it static means it only has to be declared once, even if there are many instances of the object.
static propTypes =
{
name: PropTypes.string,
age: PropTypes.number
}
In order to use the PropTypes class, we need to include the javascript file below:
<script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script> <!-- Note: when deploying, replace "prop-types.js" with "prop-types.min.js". -->
Example using the PropTypes class (Run Example)
<!DOCTYPE html> <html> <head> <title>PropTypes example</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Language" content="en" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script> <!-- Note: when deploying, replace "prop-types.js" with "prop-types.min.js". --> <!-- When including external js files, remember to make the type="text/babel", as shown below --> <!-- <script type="text/babel" src="myExternalFile.js"></script> --> <script type="text/babel"> "use strict" class PeopleItem extends React.Component { static propTypes = { name: PropTypes.string } constructor(props) { super(props) } render() { return ( <li key={this.props.name}>{this.props.name} </li> ) } } class People extends React.Component { static propTypes = { names: PropTypes.array } constructor(props) { super(props) } render() { return ( <ul> {this.props.names.map(name => <PeopleItem key={name} name={name} />)} </ul> ) } } </script> </head> <body> <h2>Multiple Components example</h2> <div id="container"></div> <!-- This code needs to be at the bottom of the BODY element --> <script type="text/babel"> ReactDOM.render(<People names = {["Alice", "Brian", "Colm", "Deirdre"]}/>, document.getElementById("container")) </script> </body> </html>
Change the propTypes in either component in the above example and run the code. Observe the console output and you will see that the invalid propType is identified.
Modify the code from the countries table examples to allow a user to click on a row and see some information about the selected country, as shown here.Your code must include propTypes for all props.
Hint: You will need to create a TableRow and a Modal component class.
Default values can be set for properties. If no property is supplied, then the default property will be used. To set default values, use defaultProps, as shown below:
static defaultProps = { country:"Ireland" }
Example with defaultValues (Run Example)
<!DOCTYPE html> <html> <head> <title>Default Properties example</title> <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> <meta http-equiv="Content-Language" content="en" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script> <!-- Note: when deploying, replace "development.js" with "production.min.js". --> <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script> <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script> <script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script> <!-- Note: when deploying, replace "prop-types.js" with "prop-types.min.js". --> <!-- When including external js files, remember to make the type="text/babel", as shown below --> <!-- <script type="text/babel" src="myExternalFile.js"></script> --> <script type="text/babel"> "use strict" class Country extends React.Component { static propTypes = { country: PropTypes.string } static defaultProps = { country:"Ireland" } constructor(props) { super(props) } render() { return ( <h1> {this.props.country} </h1> ) } } </script> </head> <body> <h2>Multiple Components example</h2> <div id="container"></div> <!-- This code needs to be at the bottom of the BODY element --> <script type="text/babel"> ReactDOM.render(<Country country = {"Poland"}/>, document.getElementById("container")) </script> </body> </html>
If you remove the country = {"Poland"} arguement in the code above, then the defaultProps will set the country to Ireland.
Copyright Derek O' Reilly, Dundalk Institute of Technology (DkIT), Dundalk, Co. Louth, Ireland.