Contents
data/authors/Paul Logan.json

Creating a React.js driven web page

This is the second post in the series.

React.js has been successfully added to out Asp.Net MVC web app in part 1.

I will now demonstrate the usage of React.js with a simple user maintenance screen, displaying a list of existing users that can be edited individually.

Displaying data in a React component

Here is our user listing page:

The user listing screen
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@{
    ViewBag.Title = "React MVC Demo";
}
<h2>@ViewBag.Title.</h2>
<style>...</style>
<div id="UsersListContainer" style="display:none;margin-top:20px;">	
    <table style="width:75%">
        <thead>
            <tr>
                <th>User Name</th>
                <th>Display Name</th>
                <th>Enabled</th>
                <th></th>
            </tr>
        </thead>
        <tbody id="userRecords">
        </tbody>
    </table>
</div>
<div id="EditRecord" style="width:fit-content;display:none"></div>

<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
<script src="~/Scripts/ReactjsMVC/reactDemo.js" type="module"></script>

A couple of noteworthy points:

  • Note that the js file reference for reactDemo.js highlighted above refers to the compiled js file that Babel creates in the Scripts folder. If you come across the following error in the console, then you could well be pointing the web page to the pre-compiled jsx file:
“Uncaught SyntaxError: Unexpected token <”
  • The reactDemo.js script tag has a type of module - this is necessary because I will be using the imports statement within that js file, and they can only be used inside modules.

I use the global fetch() method of the javscript Fetch API to start the process of fetching the list of users from the network (my server side MVC controller action). The method returns a promise that is fulfilled once the response is available.

I extract the JSON body content from the promise’s resolved response object, then call React’s DOM render() method to render the React user summary component into the DOM, passing in the JSON array of users as a parameter. This top-level API is available from the ReactDOM global, because I am loading React from a <script> tag.

Finally, I display user summary container, which shows the populated table of users.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let users;

fetch('/Home/AppUsers').then(response => {
    response.json().then(data => {
        users = data;
        ReactDOM.render(<UserSummary users={users} />, document.getElementById('userRecords'));
        document.getElementById('UsersListContainer').style.display = "";
    })
});

function UserSummary(params) {
    users = params.users;
    users.forEach(u => u.EnabledSymbol = u.Enabled ? "✔️" : "❌");
    return (
        users.map((u, index) => (
            <tr key={u.UserName}>
                <td>{u.UserName}</td>
                <td>{u.DisplayName}</td>
                <td>{u.EnabledSymbol}</td>
                <td>
                    <button onClick={() => EditUser(u.UserName)}>Edit</button>
                </td>
            </tr >
        ))
    );
}

Editing form data in a React component

Here is the screen shown after clicking the Edit button for a user:

The user editing screen

The Edit button is wired up to call the EditUser function when clicked. This function will retrieve the user from the user list and pass it into our next React component for editing the user details, hiding the user summary component as well.

31
32
33
34
35
36
function EditUser(userName) {
    const userDetails = users.find(uu => uu.UserName === userName);
    ReactDOM.render(<User user={userDetails} />, document.getElementById('EditRecord'));
    $('#UsersListContainer').hide();
    $('#EditRecord').show();
}

The User editing React component is shown below. I extract the user details from the props parameter passed in. I then pass these details as the initial state to the useState hook, which returns the current state and a function that updates it. These returns are assigned to variables using destructuring assignment syntax.

Handlers are added for input changes and form submission, and finally the component is rendered with the return JSX.

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
function User(props) {
    const editedUser = props.user;
	//declaration of state and state changing function variables:
    const [values, setValues] = React.useState(editedUser);

    const handleInputChange = (e) => {
        setValues(prev => ({ ...prev, [e.target.name]: e.target.value }))
    };

    const handleCheckBoxChange = (e) => {
        setValues(prev => ({ ...prev, [e.target.name]: e.target.checked }))
    };

    const handleSubmit = (event) => {
        if (event) {
            event.preventDefault();
            $.ajax({
            url: '/Home/UpdateUser',
            data: { editedUser: values },
            type: 'POST',
            success: function () {
                const originalUser = users.find(uu => uu.UserName === editedUser.UserName);
                originalUser.DisplayName = values.DisplayName;
                originalUser.Enabled = values.Enabled;
                ReactDOM.render(<UserSummary users={users} />, document.getElementById('userRecords'));
                $('#UsersListContainer').show();
                $('#EditRecord').hide();
            },
            successFalse: function (data) {
                alert("Error updating user");
            }
        });
        }
    };

    return (
        <form id="UserForm" onSubmit={handleSubmit} >
            <div className="maintSection">
                <label>User Name</label><label>{values.UserName}</label>
                <label>Enabled</label><input type="checkbox" name="Enabled" checked={values.Enabled} onChange={handleCheckBoxChange} />
                <label>Display Name</label><input type="text" name="DisplayName" className="form-control" value={values.DisplayName} onChange={handleInputChange} required />
            </div>
            <button type="submit" className="btn btn-success">Save</button>
        </form>
    );
}

The data-binding magic is provided by React.js' Controlled Components. A controlled component is created by binding the input value to the state variable (named values above), and an onChange event to change the state as the input value changes - in this case, the setValues function in the handleInputChange and handleCheckBoxChange handlers.

Submitting form data from a React component

Regards,

Paul.