React

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:

ReactDom
The ReactDom class can access React's virtual DOM. This virtual DOM provides all of the functionality of the normal DOM, but renders faster than a normal DOM.
React
The React class is used to create the elements that are placed in a ReactDom object.

In order to use React, we only need to use one method from each of the two classes:

React.createElement()

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:

type
Type of the HTML element. This must be a string (for example "div", "p", "h1").
props
This holds the properties of the element. These are the same as the arguments in a normal HTML element (for example name, style, class and event handlers).
The props must be held in an object (for example {name:"helloDiv", style: {color: "red", border:"thin solid #aaa"}})
Styles are represented as an object within the props object, so style needs its own, additional, {} curly brackets (for example {name:"helloDiv", style: {color: "red", border:"thin solid #aaa"}}).
children
This holds the element's content - such as the text "Hello World" in the code belowt. The element's content can also include one or more child elements. For example, a form might contain several input elements.

For example:

React.createElement("div", {name:"helloDiv", style: {color: "red", border:"thin solid #aaa"}}, "Hello World")

ReactDom.render()

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

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 tag 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 versus React.createElement()

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("loginFormContainer")
               )
</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("loginFormContainer"))
</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>
#loginForm 
{
    width:300px;
    border:thin solid #aaaaaa;
    padding: 20px;
    margin-left: auto;
    margin-right: auto;
    border-radius: 5px;
    background: #f7f7f7;
}

#loginForm input
{
    display: block;
    width: 100%;
    padding: 5px 10px;
    margin: 10px 0;
    border-radius: 5px;
    border: 1px solid #ddd;
}

#loginForm input 
{
    box-sizing: border-box;
}

#loginForm input:focus
{
    border-radius: 0px;
    border-color:blue;
}

#loginForm 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;
}

#loginForm input[type=submit]:hover 
{
    background-color: #669509;
}

#loginForm input[type=submit]:disabled 
{
    background-color: #dbf99f;
    color: #fcc;
}
</style>


</head>
<body>
<h2>Example using React.createElement() instead of JSX</h2>

<div id="loginFormContainer"></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("loginFormContainer")
               )
</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>
#loginForm 
{
    width:300px;
    border:thin solid #aaaaaa;
    padding: 20px;
    margin-left: auto;
    margin-right: auto;
    border-radius: 5px;
    background: #f7f7f7;
}

#loginForm input
{
    display: block;
    width: 100%;
    padding: 5px 10px;
    margin: 10px 0;
    border-radius: 5px;
    border: 1px solid #ddd;
}

#loginForm input 
{
    box-sizing: border-box;
}

#loginForm input:focus
{
    border-radius: 0px;
    border-color:blue;
}

#loginForm 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;
}

#loginForm input[type=submit]:hover 
{
    background-color: #669509;
}

#loginForm input[type=submit]:disabled 
{
    background-color: #dbf99f;
    color: #fcc;
}
</style>


</head>
<body>
<h2>Example using JSX instead of React.createElement()</h2>

<div id="loginFormContainer"></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("loginFormContainer"))
</script>

</body>
</html>

HTML & JSX

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>

 

Client-Side React

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 Forms

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>  -->

<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(index => formInputsState[index])) 
        {
            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>


<style>
#loginForm 
{
    width:300px;
    border:thin solid #aaaaaa;
    padding: 20px;
    margin-left: auto;
    margin-right: auto;
    border-radius: 5px;
    background: #f7f7f7;
}

#loginForm input
{
    display: block;
    width: 100%;
    padding: 5px 10px;
    margin: 10px 0;
    border-radius: 5px;
    border: 1px solid #ddd;
}


#loginForm div.error
{
    padding:5px;
    background-color:#fbb;
    border:thin solid #aaa;  
    border-radius:5px;
}

#loginForm input 
{
    box-sizing: border-box;
}

#loginForm input:focus
{
    border-radius: 0px;
    border-color:blue;
}

#loginForm 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;
}

#loginForm input[type=submit]:hover 
{
    background-color: #669509;
}
</style>


</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 (£!#€$%^&amp;*)</p>
<div id="loginFormContainer"></div>

<!-- This code needs to be at the bottom of the BODY element -->
<script type="text/babel">
ReactDOM.render(<LoginForm />, document.getElementById("loginFormContainer"))
</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.

componentDidMount()

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:

focus()

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 tag, as shown below:

            <input
                name = "email"
                className = {formInputsState.email ? "" : "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>
<meta charset="UTF-8" />
<title>Registration form 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>


<!-- 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 RegistrationForm extends React.Component 
{
    constructor() 
    {
        super()

        this.state = 
        {
            email: "",
            password: "",
            confirmPassword:""
        }
    }


    componentDidMount()
    {
        this.inputToFocus.focus()
    }


    handleChange = e => 
    {
        this.setState({ [e.target.name]: e.target.value })
    };   
    
    
    handleSubmit = e => 
    {
        const formInputsState = this.validate()
    
        if (Object.keys(formInputsState).every(index => formInputsState[index])) 
        {
            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()
    {    
        return this.state.password.length > 0
    }


    validateConfirmPassword()
    {    
        return (this.state.password === this.state.confirmPassword)
    }


    validate() 
    {
        return {
            email: this.validateEmail(),
            password: this.validatePassword(),
            confirmPassword: this.validateConfirmPassword()
        }
    }


    render() 
    {
        const formInputsState = this.validate()
        const inputsAreAllValid = Object.keys(formInputsState).every(index => formInputsState[index])
            
        return (
          <form id="regitrationForm" onSubmit = {this.handleSubmit}>
            <input
                name = "email"
                className = {formInputsState.email ? "" : "error"}
                type = "text"
                placeholder = "Email"
                value = {this.state.email}
                onChange = {this.handleChange}
                ref = {input => this.inputToFocus = input} 
            />
               
            <input
              name = "password"
              className = {formInputsState.password ? "" : "error"}
              type = "password"
              placeholder = "Password"
              value = {this.state.password}
              onChange = {this.handleChange}
            />
        
            <input
              name = "confirmPassword"
              className = {formInputsState.confirmPassword ? "" : "error"}
              type = "password"
              placeholder = "Confirm password"
              value = {this.state.confirmPassword}
              onChange = {this.handleChange}
            />
        
            <input 
              type = "submit" 
              value = "Register" 
              disabled = {!inputsAreAllValid}
            />
          </form>
        )
    }
}
</script>


<style>
#regitrationForm 
{
    width:300px;
    border:thin solid #aaaaaa;
    padding: 20px;
    margin-left: auto;
    margin-right: auto;
    border-radius: 5px;
    background: #f7f7f7;
}

#regitrationForm input
{
    display: block;
    width: 100%;
    padding: 5px 10px;
    margin: 10px 0;
    border-radius: 5px;
    border: 1px solid #ddd;
}


#regitrationForm input.error
{
    border-color: red;
}

#regitrationForm div.error
{
    color:red;
    margin-bottom:2em;
}


#regitrationForm input 
{
    box-sizing: border-box;
}

#regitrationForm input:focus
{
    border-radius: 0px;
    border-color:blue;
}

#regitrationForm 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;
}

#regitrationForm input[type=submit]:hover 
{
    background-color: #669509;
}

#regitrationForm input[type=submit]:disabled 
{
    background-color: #dbf99f;
    color: #fcc;
}
</style>


</head>
<body>
<h2>Registration form example</h2>

<div id="registrationFormContainer"></div>


<!-- This code needs to be at the bottom of the BODY element -->
<script type="text/babel">
ReactDOM.render(<RegistrationForm />, document.getElementById("registrationFormContainer"))
</script>

</body>
</html>

Write registration form code, so that the user is required to enter a password that is at least ten-digits long and contains at least one lowercase letter, one uppercase letter, one digit and one of the following characters (£!#€$%^&*), as shown here.

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.

Lists

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="listContainer"></div>

<!-- This code needs to be at the bottom of the BODY element -->
<script type="text/babel">
ReactDOM.render(<NameList />, document.getElementById("listContainer"))
</script>

</body>
</html>

 

 

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="listContainer"></div>

<!-- This code needs to be at the bottom of the BODY element -->
<script type="text/babel">
ReactDOM.render(<NameList />, document.getElementById("listContainer"))
</script>

</body>
</html>

Check the browser code inspector (F12). It will give a warning that relates to the list items not having unique keys.

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>  -->
<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>


<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>
</head>
	
<body>
<h2>JSON key example</h2>

<div id="CountriesContainer"></div>


<!-- This code needs to be at the bottom of the BODY element -->
<script type="text/babel">
ReactDOM.render(<JSONForm />, document.getElementById("CountriesContainer"))
</script>


</body>
</html>

Check the browser code inspector (F12). It does not give a unique keys warning.

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

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="listContainer"></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("listContainer"))
</script>

</body>
</html>

Multiple Components

If a React component is only used to display output (i.e. it does not have any state), then it is called as a presentational component. If a component has state (e.g. a form with inputs), it is called as a container component.

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="listContainer"></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("listContainer"))
</script>

</body>
</html>

getDerivedStateFromProps

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.

    static getDerivedStateFromProps(props, state) 
    { 
        return {someItem: props.someItem} 
    }

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.

static getDerivedStateFromProps(props, state) 
    { 
        return(state.someItem !== props.someItem?{someItem: props.someItem}:null) 
    }

Write code to allow a user to filter a JSON set of data, as shown here. Use the dataset from this link, which is based on the dataset from https://restcountries.eu/rest/v2).
Hint: Put the table into its own component class.

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.

Property Types

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="listContainer"></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("listContainer"))
</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.

Default Properties

Default values can be used in the case that a property is missing from a component. To set default values, use defaultProps, as shown below:

    static defaultProps = 
    {
        name:"Error: No name passed into PeopleItem object"            
    }

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 PeopleItem extends React.Component 
{
    static propTypes = 
    {
        name: PropTypes.string
    }
    
    
    static defaultProps = 
    {
        name:"Error: No name passed into PeopleItem object"            
    }
    
        
    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
    }
    
    
    static defaultProps = 
    {
        names:["Error: No array of names passed into People object"]            
    }
    
    
    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="listContainer"></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("listContainer"))
</script>

</body>
</html>

Remove the names = {["Alice", "Brian", "Colm", "Deirdre"]} attribute from the <People /> tag in the example above and run the code. The error message "Error: No array of names passed into People object" should be output.

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 and defaultProps for all props.
Hint: Make a TableRow and a Modal component class.

 
<div align="center"><a href="../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>