To Do: Create Your First React Application – Part II

Previously, we looked at getting set up creating a React “to do” application by installing our Node.js driven build tools and laying out some introductory components of code. In what follows we will continue on where we left off.

Adding “To Do” Items & Handling User Input

So far we have just looked how to display our components. Rendering data out to our page is all well and good, but to make our application truly dynamic we are going to need to implement a way to add and remove items to and from our list as well as edit them should we need to make any changes to any of the properties. So let’s update the code in TodoList.js to the following…

import {react} from 'react';
import {Todo} from './Todo';

export class TodoList extends React.Component {
    
    constructor(){
        super(...arguments);
        this.state = {
            todos: this.props.data,
            addTodoTitle: '',
            addTodoSummary: ''
        }
    }
    
    addTodoTitleChange(e){
        this.setState({
            addTodoTitle: e.target.value
        });        
    }
    
    addTodoSummaryChange(e){
        this.setState({
            addTodoSummary: e.target.value
        });           
    }    
        
    addTodo(){
        this.state.todos.push({
            id: this.state.todos[this.state.todos.length - 1].id + 1,
            title: this.state.addTodoTitle,
            summary: this.state.addTodoSummary,
            done: false
        });

        this.setState({
            todos: this.state.todos,
            addTodoTitle: '',
            addTodoSummary: ''
        });        
    }
    
    render() {
        
        var items = this.props.data.map((item, index) => {
            return (
                <Todo key={item.id} title={item.title} summary={item.summary} completed={item.done} />
            );        
        });
        
        return(
            <div>
                <ul>{items}</ul>
                
                <h3>Add New Item:</h3>
                <label>Title</label> <input type="text" value={this.state.addTodoTitle} onChange={() => this.addTodoTitleChange()} />
                <label>Description</label> <textarea value={this.state.addTodoSummary} onChange={() => this.addTodoSummaryChange()}></textarea>
                <button onClick={() => this.addTodo()}>Add New Item</button>
            </div>
        )
    }
};

So let’s discuss what is going on here. We have added 2 new values to our initial state object and we have created a form that will allow us to add a new “to do” item to our list. When we enter text into these fields the functions to update the state will run and we will update these values with setState by getting the current value of the form element we are editing off of the event that is passed into the function (e.target.value). Once we have filled out the form with the title and summary of the new item that we want, we can click the “Add New Item” button which, as we can see from our “onClick” handler, will run the “addTodo” function. If we look at this function we can see the code that we will need to add a new item to our list. Recall that we have to give each individual item an id and no two items can have the same id. So to generate a new id we will look at what the id value of the last item in our array is and just add 1 to that. That is an easy way to ensure that the new items that we add are always unique. We get the title and the summary from this.state which we have been setting in our text input handler functions. Lastly, we just set the initial value of the “done” flag to false since adding a new to do to the list is presumably not done yet. All of this data is passed into the common JavaScript Array object’s “push” method. We then update the state of our “todos” and then clear out the input text box and textarea in our form to get them ready to receive another value.

So build your code with $ webpack (or whatever task runner you are using) and try it out. You can see how we can now add new items to our list! Pretty slick!

Improving Our Code

Our example above works but you may have noticed something problematic with our implementation. We have individual handler functions for the two text elements in our “Add New Item” form. We also have separate properties that we use for setState. This is fine for this simple example, but what if we had a component with many different properties and values with many different fields that needed to be filled out? If we were to take the same approach as our example above, we would have to create an individual function for each and every property. Not too efficient!

So what can we do to mitigate this potential scenario of our React application scaling to incorporate more complex objects? We should abstract this functionality into one handler function and also pass in the property that we need to update depending on the form element we are currently changing. So let’s change the code in our TodoList.js file to the following…

import {react} from 'react';
import {Todo} from './Todo';

export class TodoList extends React.Component {
    
    constructor(){
        super(...arguments);
        this.state = {
            todos: this.props.data,
            newItem: {
                title: '',
                summary: ''
            }
        }
    }
    
    handleChange(key, event){
        var change = {
            title: this.state.newItem.title,
            summary: this.state.newItem.summary
        };
        change[key] = event.target.value;
        
        this.setState({           
            newItem: change        
        });               
    }
    
    addTodo(){
        this.state.todos.push({
            id: this.state.todos[this.state.todos.length - 1].id + 1,
            title: this.state.newItem.title,
            summary: this.state.newItem.summary,
            done: false
        });

        this.setState({
            todos: this.state.todos,
            newItem: {
                title: '',
                summary: ''
            }
        });        
    }
    
    render() {
        
        var items = this.props.data.map((item, index) => {
            return (
                <Todo 
                    key={item.id} 
                    title={item.title} 
                    summary={item.summary} 
                    completed={item.done} />);        
        });
        
        return(
            <div>
                <ul>{items}</ul>
                
                <h3>Add New Item:</h3>
                <label>Title</label> <input type="text" value={this.state.newItem.title} onChange={(e) => this.handleChange('title', e)} />
                <label>Description</label> <textarea value={this.state.newItem.summary} onChange={(e) => this.handleChange('summary', e)}></textarea>
                <button onClick={() => this.addTodo()}>Add New Item</button>
            </div>
        )
    }
};

So what have we done here? For starters, instead of creating individual properties for each value our component will hold we will hold we put these into one object called “newItem.” That way if our component were ever to change we could just add to this single object rather than creating separate properties directly on the state object. Note too that we have also created a single handler function called “handleChange” to handle the change to any of our form elements. We have updated the format of our “onChange” events. Now we are passing a second parameter in to the handler function. This serves to tell React which field is currently being updated. In the handler function, we then construct the object to get the current state of all the values in our component and then we use the key that we passed in to change the relevant property.

Now if we build and run again, we can see that we have the same functionality, but we have refactored our code in such a way that we can scale our components to add new properties in the future. This is much better for us in the long run.

Validation

Validation of data is an important aspect of any application. In React we are mostly dealing with client-side validation. It is common knowledge within software development that you can never trust client-side validation alone because anyone on the client side can use developer tools and change the code that runs on the client to circumvent any validation that happens on the client. So any data that we send up to a server from a React application should also always always always be validated on the server as well. But chances are most of the users of your app will not be malicious individuals cracking open their developer tools and trying to hack their way into your system, so we will want to make sure that the good/normal users of your application have a good user experience. Part of this involves making sure that any input(s) that the users of your app make is checked and validated on the client side so that the app does not have to wait to make an entire round trip up to the server before informing the user that they have done something they were not supposed to… forgot to fill out a required form field, entered incorrect data in a form field such as an improperly formatted e-mail address or phone number, or any number of other common errors that can happen when handling user input. Again, this does not substitute for always validating your data on the server side as well, but it is a useful first line of defense and an important element to an enjoyable experience for your users. It also can reduce the traffic load on your server(s). If you can prevent sending a request until all the data that you’re getting from the client is good and valid, you should only need to make the particular request that you need to make to the server one time!

So all that being said, client-side validation definitely has its uses and it is worth making the effort to put it in for the aforementioned reasons. Fortunately, React has built in structure to validate props that we pass into our components and make sure that required data is present and that the data is of the correct type. We can do a quick test to demonstrate that validation aspects of React are present.

To start out, in our Todo.js file place the following at the bottom of the file (outside of the class declaration)…

...
} // end of Todo class

Todo.propTypes = {
    title: React.PropTypes.string.isRequired
}

What we have declared here is that the title property is required and must be present on the component. To test out that this is working, edit the “Todolist.js” file by removing the “title” property in the render method…

...
    render() {
        
        var items = this.props.data.map((item, index) => {
            return (
                <Todo 
                    key={item.id} 
                    summary={item.summary} 
                    completed={item.done} 
                    remove={() => this.removeTodo(index)} />);        
        });
        
        return(
            <div> 
...

If we build and run our application we see the following warning message logged to our developers…

Warning: Failed propType: Required prop `title` was not specified in `Todo`. Check the render method of `TodoList`.

Because we have specified that we are expecting this property called “title” React issues this warning ahead of time. Now that we know that React is picking up on validation, we can go ahead and put the “title” property that we just deleted back in again. Now if we build and run the file again we see that the warning message goes away!

Let’s build upon this and explore some additional aspects of component validation. In addition to specifying which properties are required, we can also specify which data types we are expecting as well. We know that in rendering our list of “to do” components we are expecting an array of objects with different properties that we use to render out in the list. In our “TodoList.js” file, let’s add the following at the bottom (again, after the closing “}” for the class definition).

...
}
TodoList.propTypes = {
    data: React.PropTypes.arrayOf(React.PropTypes.object)
}

Here we have specified that the property that we pass into data in our “App.js” file needs to be an array of objects. Then in the Todo.js file we can build on what we did earlier with the “title” prop and add a few more rules around the expected values.

Todo.propTypes = {
    title: React.PropTypes.string,
    summary: React.PropTypes.string,
    completed: React.PropTypes.bool
}

There are many other aspects of validation that you can incorporate into your application and how you incorporate things really depends on how your application is structured. We will not cover every single validation case in detail here, but the preceding sections should give you a good place to start when you want to implement validation into your components. For more detail on this you can consult the React documentation.

Removing “To Do” Items & Passing Click Events From Child Component to a Parent Component

So now that we have the ability to add “to do” items, let’s take a look at how we can implement the functionality to remove items. Change the code in TodoList.js to the following…

import {react} from 'react';
import {Todo} from './Todo';

export class TodoList extends React.Component {
    
    constructor(){
        super(...arguments);
        this.state = {
            todos: this.props.data,
            newItem: {
                title: '',
                summary: ''
            }
        }
    }
    
    handleChange(key, event){
        var change = {
            title: this.state.newItem.title,
            summary: this.state.newItem.summary
        };
        change[key] = event.target.value;
        
        this.setState({           
            newItem: change        
        });               
    }
    
    addTodo(){
        this.state.todos.push({
            id: this.state.todos[this.state.todos.length - 1].id + 1,
            title: this.state.newItem.title,
            summary: this.state.newItem.summary,
            done: false
        });

        this.setState({
            todos: this.state.todos,
            newItem: {
                title: '',
                summary: ''
            }
        });        
    }
    
    removeTodo(index) {
        this.state.todos.splice(index, 1);

        this.setState({
            todos: this.state.todos
        });  
    }
    
    render() {
        
        var items = this.props.data.map((item, index) => {
            return (
                <Todo 
                    key={item.id} 
                    title={item.title} 
                    summary={item.summary} 
                    completed={item.done} 
                    remove={() => this.removeTodo(index)} />);        
        });
        
        return(
            <div>
                <ul>{items}</ul>
                
                <h3>Add New Item:</h3>
                <label>Title</label> <input type="text" value={this.state.newItem.title} onChange={(e) => this.handleChange('title', e)} />
                <label>Description</label> <textarea value={this.state.newItem.summary} onChange={(e) => this.handleChange('summary', e)}></textarea>
                <button onClick={() => this.addTodo()}>Add New Item</button>
            </div>
        )
    }
};

As we can see we have added a “removeTodo” method that will remove the item in our “todos” array at a certain index. But there is an additional layer to this functionality that we need to explore. Each individual “to do” item needs to have its own remove button. Otherwise, how will the user know which item he/she is removing? So we are actually going to have to update Todo.js as well (where the definitions of individual “to do” components are located). Before we do that make special note of the “remove” property for the Todo component in the render method above. We are essentially passing our “removeTodo” method down into each component. This is how we get cross-component communication and it will become increasingly more important later on.

On that note, change the code in Todo.js to the following…

import {react} from 'react';

export class Todo extends React.Component {
    
    constructor(){
        super(...arguments);
        this.state = {
            completed: this.props.completed,
        }
    } 
    toggleCompleted(){
        this.setState({completed: !this.state.completed})
    }
    render() {
        
        return(
            <li>
                <h2>{this.state.title}</h2><p>{this.state.summary}</p> <input type="checkbox" checked={this.state.completed} onChange={() => this.toggleCompleted()} />
                <button onClick={this.props.remove}>Remove</button>
            </li>
        )  
    }
};

As we can see here we have added a “Remove” button and wired the “onClick” event to this.props.remove. Recall from above that the “remove” property was a function on the “TodoList” component. So when we are clicking that button we are essentially calling up into that function. This code works and our click event travels up into the parent component and runs the “removeTodo” function there and it knows which item the event came from and its index in the list. Pretty neat, eh? This is a basic example of how we can pass events from child components to parent components.

As we have seen that using props, state, and event handlers properly is an essential part of developing React applications. There are many different places we can put our props and our event handlers and how you go about structuring things is entirely up to you. It can seem a bit complicated or overwhelming at first but after working with React for awhile you will begin to see some of the wisdom behind it. If you ever find yourself in a place where you are not sure what to do, you should always keep in mind this simple ancient React proverb…

Pass properties down, pass events up.

We saw this with our functionality to remove items from our list. We passed our function that we wanted to run down as a property into the Todo component, and linked the two when we passed the button click for the “Remove” button up to this function on the parent component. That is the general pattern to follow when working with React and communication between components. If you heed this wisdom, you will be very wise and build React applications that will be maintainable enough to endure for all eternity, young grasshopper.

Editing “To Do” Items

There is one final piece of functionality that we need to implement to have a useful React application and that is the ability to edit items in place. As it turns out all of the pieces that we need to implement this can be done in the Todo component. We do not need to keep track of anything at the TodoList level because all of the data we want to change resides within each item. So change the code of Todo.js to the following…

import {react} from 'react';

export class Todo extends React.Component {
    
    constructor(){
        super(...arguments);
        this.state = {
            title: this.props.title,
            summary: this.props.summary,
            completed: this.props.completed,
        }
    } 
    toggleCompleted(){
        this.setState({completed: !this.state.completed})
    }
    editTodo(key, event) {
        
        var change = {
            title: this.state.title,
            summary: this.state.summary
        };
        change[key] = event.target.value;
        this.setState(change);       
    }       
    render() {
        
        return(
            <li>
                <h2>{this.state.title}</h2>
                <p>{this.state.summary}</p> 
                <input type="checkbox" checked={this.state.completed} onChange={() => this.toggleCompleted()} />
                
                <input type="text" value={this.state.title} onChange={(e) => this.editTodo('title', e)} /> <textarea value={this.state.summary} onChange={(e) => this.editTodo('summary', e)}></textarea>                 
                
                <button onClick={this.props.remove}>Remove</button>
            </li>
        )  
    }
};

As we can see we have added a textbox and a textarea where we can edit the title and the summary. Similar to what we did earlier with our code to add an item, we create a common handler function (editTodo) for all of the fields that are editable and pass in a string with the name of the property we want to update. Notice that when we edit each individual item, the display text of each individual title and summary also change because they are data bound to this value.

Conditionally Displaying Elements Within Components

This editing functionality works fine from a functional standpoint, but as far as UX (user experience) goes it is not very good. It is ugly having a big form in the middle of each item as part of the display and one can only imagine how big and bloated things would get if we have more properties and or more items. We need to have a way to only show these when that particular item is being edited. For this we can again turn back to state and see if we can add an “edit mode” flag to our component so that we will only display the editing form elements when this “edit mode” flag is set to true. So let’s update the code in our Todo.js file to the following…

import {react} from 'react';

export class Todo extends React.Component {
    
    constructor(){
        super(...arguments);
        this.state = {
            title: this.props.title,
            summary: this.props.summary,
            completed: this.props.completed,
            inEditMode: false
        }
    } 
    toggleCompleted(){
        this.setState({completed: !this.state.completed})
    }
    toggleEditMode(){
        this.setState({ inEditMode: !this.state.inEditMode });
    }
    editTodo(key, event) {
        
        var change = {
            title: this.state.title,
            summary: this.state.summary
        };
        change[key] = event.target.value;
        this.setState(change);       
    }       
    render() {
        
        return(
            <li>
                {this.state.inEditMode ? <div><input type="text" value={this.state.title} onChange={(e) => this.editTodo('title', e)} /> <textarea value={this.state.summary} onChange={(e) => this.editTodo('summary', e)}></textarea></div> : <div><h2>{this.state.title}</h2><p>{this.state.summary}</p> <input type="checkbox" checked={this.state.completed} onChange={() => this.toggleCompleted()} /></div>}
                <button onClick={() => this.toggleEditMode()}>{this.state.inEditMode ? 'Stop Editing' : 'Edit'}</button>
                <button onClick={this.props.remove}>Remove</button>
            </li>
        )  
    }
};

As we can see we have added the flag “inEditMode” to our state that we toggle when we click our newly added “Edit” button which runs the “toggleEditMode” function. What does this do? In our component display we are using a ternary operator to display the form elements if the item is in “edit mode.” If the particular item is not in “edit mode” we just display the title and summary as we had been doing before. This implementation is one of the ways that is suggested to conditionally display parts of a React component in the React documentation. As this piece grows with more complex templates doing things this way could get a little bit bloated. So an alternative to this, as is suggested in the React documentation, is to do the conditional check for “inEditMode” just outside of the render function’s return method like so…

import {react} from 'react';

export class Todo extends React.Component {
    
    constructor(){
        super(...arguments);
        this.state = {
            title: this.props.title,
            summary: this.props.summary,
            completed: this.props.completed,
            inEditMode: false
        }
    } 
    toggleCompleted(){
        this.setState({completed: !this.state.completed})
    }
    toggleEditMode(){
        this.setState({ inEditMode: !this.state.inEditMode });
    }
    editTodo(key, event) {
        
        var change = {
            title: this.state.title,
            summary: this.state.summary
        };
        change[key] = event.target.value;
        this.setState(change);       
    }       
    render() {
        var display;
        if(this.state.inEditMode) {
            display = <div><input type="text" value={this.state.title} onChange={(e) => this.editTodo('title', e)} /> <textarea value={this.state.summary} onChange={(e) => this.editTodo('summary', e)}></textarea></div>;
        } else {
            display = <div><h2>{this.state.title}</h2><p>{this.state.summary}</p> <input type="checkbox" checked={this.state.completed} onChange={() => this.toggleCompleted()} /></div>
        }
        
        return(
            <li>
                {display}
                <button onClick={() => this.toggleEditMode()}>{this.state.inEditMode ? 'Stop Editing' : 'Edit'}</button>
                <button onClick={this.props.remove}>Remove</button>
            </li>
        )  
    }
};

This approach is a bit cleaner for more complex displays with more data values to keep track of.

Updating the Parent Component after State Changes on the Child

As we have seen the state in our individual to do items (title, summary, etc.) is stored on each individual “to do” item and the list of them is stored on the parent “TodoList” component. However, let’s take a brief look at something involved with this interaction that will be very important to remember when developing React applications. It is something that might seem a little unexpected at first but not all state changes in a child necessarily trigger state changes in the parent. This is especially noticeable when storing lists of child components in a property on the state object that is an array. In our case, our “TodoList” component has the “todos” property that stores the array of “Todo” components.

As it turns out we will actually have to specifically declare what we want to do on the parent when the child updates because there are certain instances when the parent state array is not aware of what has changed on the child component. Let’s take a look at when and why this might occur and what we can do about it.

In our “addTodo” method of our “TodoList” component, add the following at the bottom of this method such that our method looks like the following…

addTodo(){
    this.state.todos.push({
        id: this.state.todos[this.state.todos.length - 1].id + 1,
        title: this.state.newItem.title,
        summary: this.state.newItem.summary,
        done: false
    });

    this.setState({
        todos: this.state.todos,
        newItem: {
            title: '',
            summary: ''
        }
    });

    console.log(this.state.todos)        
}

Here we are logging just the current array of “to do” items whenever we add a new item. Now, after webpack recompiles this code let’s refresh our browser. As expected, we will see our original list of “to do” items. Edit the first existing item in our list by changing the title from “Eat Breakfast” to “Eat Brunch” or something like that. You can change the summary as well if you like, but any change will suffice here for purposes of demonstration. When you are done click the “Stop Editing” button.

As we can see the array of objects is logged to the console and there will be 4 items as we would expect [Object, Object, Object, Object]. However, look and see what happens if we expand and examine the first object (the one we originally edited). Surprisingly, the title still says “Eat Breakfast,” even though in the UI it says “Eat Brunch” because we changed it earlier. What is going on there?

This is one of those cases where parent component does not pick up changes in the child. As far as the parent component sees, there is still an object at index 0 of the “todos” property on the state object. Even though we changed the properties of that object, there was no change in the array from the parent’s point of view. When a new object to the array, that change *is* actually picked up because the array is now 1 item longer so the new item appears in the array. Long story short, there are steps we need to take to make sure that the parent state that stores the list of child components gets updated when we edit individual items. If we were to go to save our list to a database or something like that, we would want to be sure that we have all the up to date information of all the items in our list.

The way that we will handle the updating of items is to create a method on the parent. We will then pass that method down into the child component via props and then call that method inside of the method that handles child updates. Let’s take a look at what this looks like when implemented…

Add the following method to the “TodoList” component…

updateTodo(obj, index){
    this.state.todos[index] = {
        id: this.state.todos[index].id,
        title: obj.title,
        summary: obj.summary
    };
}

Then in the render() method, when we are looping through each “to do” item via the Array.map method, attach an update prop to each “Todo” component.

render() {
    
    var items = this.props.data.map((item, index) => {
        return (
            <Todo 
                key={item.id} 
                title={item.title} 
                summary={item.summary} 
                completed={item.done}
                update={(obj) => this.updateTodo(obj, index)}
                remove={() => this.removeTodo(index)} />);        
});
...

There is nothing special about the name “update.” We could name this prop whatever we want. Notice that this prop points to the method we had just created earlier. It will receive 2 parameters. The first will be passed up from the child “Todo” component, and the second is the index so we have a way of knowing which item in the array to update. All we end up doing is replacing the old object at this spot with the new object with updated data. We keep the id key exactly the same.

Finally, in the “Todo” component, in the place where we update individual state for the item (out editing function), we can call the update method via this.props. So add the call to our update method on the parent to youe “Todo” component…

editTodo(key, event) {
    
    var change = {
        title: this.state.title,
        summary: this.state.summary
    };
    change[key] = event.target.value;
    this.setState(change);    
    this.props.update(change);   
}

Notice our call to the “update” method which comes in as a prop. Basically all we are doing here is passing the data object that we have just changed up into our update property. This data comes in and gets passed to the parent method along with the index location where this data lives in the array.

Now if we rebuild and rerun our code we can see that when we update our individual items and then add a new item (assuming we still have the console.log in our code) we can see that whenever we edit “Todo” items, they are getting updated in the array and our data is consistent between parent and child. If we were to go save this data, we could be confident knowing it is up to date and consistent with what the user is seeing in the UI.

So that is something to be cognizant of when working with React. It is always a good thing to confirm that your state of anything on parent components match the individual child state properties.

CSS Styling & Polish in React

We will cover how to handle the visual styling (CSS) in React more in depth in future discussions, but for now it is worth doing a little bit of styling to add a bit of polish to our application here just to get our feet wet. Let’s add a stylesheet reference to our index.html file…

<!DOCTYPE html>
<html>
<head>
    <title>React</title>
    <meta charset=&quot;UTF-8&quot;>
    <meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;>
    <link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;css/style.css&quot;>    
</head>
<body>
    <div id=&quot;content&quot;></div>
    
    <script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react.js&quot;></script>
    <script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react-dom.js&quot;></script>
    <script type=&quot;text/javascript&quot; src=&quot;dist/App.js&quot;></script>      
    
</body>
</html>

And then we can add a few additional items to each of our list items that make up each individual “to do” component. Update the code in Todo.js to the following…

import {react} from 'react';

export class Todo extends React.Component {
    
    constructor(){
        super(...arguments);
        this.state = {
            title: this.props.title,
            summary: this.props.summary,
            completed: this.props.completed,
            inEditMode: false
        }
    } 
    toggleCompleted(){
        this.setState({completed: !this.state.completed})
    }
    toggleEditMode(){
        this.setState({ inEditMode: !this.state.inEditMode });
    }
    editTodo(key, event) {
        
        var change = {
            title: this.state.title,
            summary: this.state.summary
        };
        change[key] = event.target.value;
        this.setState(change);       
    }       
    render() {
        var isComplete = this.state.completed ? 'complete' : '';
        var display;
        
        if(this.state.inEditMode) {
            display = <div><input type="text" value={this.state.title} onChange={(e) => this.editTodo('title', e)} /> <textarea value={this.state.summary} onChange={(e) => this.editTodo('summary', e)}></textarea></div>;
        } else {
            display = <div className={isComplete}><h2>{this.state.title}</h2><p>{this.state.summary}</p> <input type="checkbox" checked={this.state.completed} onChange={() => this.toggleCompleted()} /></div>
        }
        
        return(
            <li>
                {display}
                <button onClick={() => this.toggleEditMode()}>{this.state.inEditMode ? 'Stop Editing' : 'Edit'}</button>
                <button onClick={this.props.remove}>Remove</button>
            </li>
        )  
    }
};

All of the changes that we have made are in the “render” function. We are using the React “className” hook to add a class to the item if the state of the current item is “completed.” If it is not in a “completed” state, this class is not added. Note that we are only doing this for the elements in “display mode.” We do not need to show this distinction when we are in “edit mode”

And then of course let’s create a “style.css” file in a “css” directory and then add some CSS to a style.css file in a “css” directory…

.complete {
    text-decoration: line-through;
}

When our “completed” class is added to the item, the “to do” item will have a “crossed-out” look to it signaling that we are done with the task. Now if we build and run this code we can see that clicking on the text box will toggle the state of whether a “to do” is completed. When a “to do” is marked as complete, the item gets crossed out by using the line-through text decoration.

We can expand on this and add a bit more CSS to enhance the display of our application. Place the following styling in the style.css file…

.complete {
    text-decoration: line-through;
}

#content label {
    display: block;
    margin-bottom: 10px;
}

#content input[type="text"] {
    margin-bottom: 10px;
    display: block;
}

#content textarea {
    margin-bottom: 10px;
    display: block;
}

#content ul {
    list-style-type: none;
    padding-left: 0;
            
}

#content li {
    margin: 5px 0;
    background: #fcfcfc;
    border: 1px solid #eee;
    padding: 10px;
}

#content li input[type="text"] {
    display: block;
    width: 100%;
    margin-bottom: 10px;
}

#content li textarea {
    display: block;
    width: 100%;
    margin-bottom: 10px;
}

#content li h2 {
    margin-top: 0;
}

Nothing too fancy, but it cleans things up a little bit.

Summary

Whew! We have come a long way and made great progress in writing our “To Do” application but we have covered a lot of the basic components of application functionality: displaying, adding, editing, and removing items. In the preceding sections we took a look at how we could do some more complex implementations in our React applications. We looked at how we could nest components within one another and we also looked at how we could handle user input — including text input and clicks — and how to send it to the UI and let React handle our UI updates. By now we are seeing how React handles a lot of this stuff for us and how its simple understandable syntax makes writing a client-side UI in JavaScript a lot cleaner with a lot less code. These will provide some valuable aspects as our applications grow and get more complex.

, , , 9bit Studios E-Books

Like this post? How about a share?

Stay Updated with the 9bit Studios Newsletter

0 Responses to To Do: Create Your First React Application – Part II

    Leave a Reply

    Your email address will not be published. Required fields are marked *