Exercise: That's Done

Exercise 6 Easy

Objective

Description

Suppose we have a list of items, each denoting a task to do, with a button next to it, as follows:

  • Learn JavaScript
  • Read story book
  • Watch documentary
  • Bake cupcakes

The button, which reads 'Done', serves to strikethrough the text of the corresponding item and then hide itself. In other words, it serves to signal that a particular task is done.

This list of items has some similarities to a typical to-do list but it isn't exactly like one. For example, there is no input field to add more items, nor is there any way to remove a task or check/uncheck it.

In this exercise, your task is to implement this program using React.

The list should be denoted via a separate component (you can name it whatever you want to but the name should obviously be meaningful), and even the list item should be denoted via a separate component.

The component representing the list should take in an items prop which is an array holding strings to be added as items in the list.

Here are a couple of points to note, when a button is clicked:

  • The strikethrough effect should be given using the <s> HTML element, not using any CSS styles.
  • The button should be hidden by removing it from the DOM, not by merely hiding it only visually (such as by using display: none).

In the end, the you should get the following program:

Live Example

View Solution

New file

Inside the directory you created for this course on React, create a new folder called Exercise-6-That's-Done and put the .js solution files for this exercise within it.

Solution

Let's start by reviewing what exactly do we need to create and then get to actually creating it.

We need to create a component that renders a <ul> list, wherein the items are taken from a given items prop. Each list item is denoted as a separate component as well, rendering an <li> element containing the text of the item and a button.

When clicked, the button serves to strikethrough the corresponding item and hide itself.

And that's it.

Now let's start coding this program, starting with perhaps the most difficult thing in programming — naming.

We could name the main component that represents the list as List, ItemList, CancellableItemList (to convey more meaning in the name), InteractiveItemList (not very cool), and so on.

We refrain from using a plural name such as Items because there is a high chance of typos happening when writing it out. That is, Items could be mistakenly typed as Item.

We particularly like the name CancellableItemList and would use that. In respect to that, each item would be called CancellableItem. Simple.

Now, let's implement both these components.

CancellableItemList takes in an items prop and renders a <ul> element with a list of CancellableItem elements. That's it.

So let's first code this and then think about CancellableItem:

function CancellableItemList({ items }) {
   return (
      <ul>
         {items.map((item, i) => (
            <CancellableItem key={i}>{item}</CancellableItem>
         ))}
      </ul>
   );
}

Recall from the previous chapter, React Rendering Lists, that when rendering a list of elements, the key prop is required on each element, with a unique value in that list. When we're not much concerned with real uniqueness, the index of each element of an array is sufficient to be used as key and that's exactly what we do above.

Moving on, notice how we provide item to each <CancellableItem> element as its children. We could use some other prop as well to accomplish this, such as item or data, but using children keeps us from having to go this way.

Now, let's get to the implementation of CancellableItem.

A CancellableItem denotes an <li> element containing a given piece of text (provided in via children), followed by a button.

First, let's get this done and then refine the code to meet our needs:

function CancellableItem({ children }) {
   return (
      <li>{children} <button>Done</button></li>
   );
}

Currently, CancellableItem doesn't maintain any state but from the description above, we know that it needs to. That's simply because either the underlying item is cancelled (or striken-through) or not cancelled — the item can be in either of two states.

Let's call this state value cancelled. Initially, it's false. The button serves to change this state to true.

When cancelled is true, the item's text ought to be shown wrapped up inside an <s> element. Recall what this means? Well, it means conditional rendering.

Both the text and the button need to be conditionally rendered:

  • Either the text is shown directly or the text wrapped up inside an <s> element, depending on the value of cancelled.
  • Either the button is shown or not shown (as in, not in the DOM), again depending on the value of cancelled.

The following code accomplishes this all:

import React, { useState } from 'react';

function CancellableItem({ children }) {
   const [cancelled, setCancelled] = useState(false);

   return (
      <li>
         {cancelled ? <s>{children}</s> : children}
         {!cancelled && <button onClick={() => setCancelled(true)}>Done</button>}
      </li>
   );
}

That's essentially it.

Let's now test this program using a dummy array provided to the items prop of CancellableItemList:

import CancellableItemList from './CancellableItemList';

function App() {
   return (
      <CancellableItemList items={[
         'Learn JavaScript',
         'Read story book',
         'Watch documentary',
         'Bake cupcakes',
      ]}/>
   );
}

Live Example

Voila! There we have it.

And this completes this exercise.