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.

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 a list item should be denoted via a separate component.

The component representing the list should take in an items prop which is an array containing all the items to be added to the list; each item is a string.

Here are a couple further 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 using some CSS (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 for this exercises and then get to actually creating it.

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

When clicked, the button serves to strikethrough the corresponding item and remove 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.

I particularly like the name CancellableItemList and would use that. In this respect then, each item of the list could 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.

So let's first code this:

function CancellableItemList({ items }) {
   return (
      <ul>
         {items.map((item, i) => (
            <CancellableItem key={i} item={item} />
         ))}
      </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.

Now, let's move over to CancellableItem.

Each item of the items list in CancellableItemList is provided to CancellableItem in the form of an item prop. A CancellableItem denotes an <li> element containing a given piece of text, followed by a 'Done' button.

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

function CancellableItem({ item }) {
   return (
      <li>{item} <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 is wrapped up inside an <s> element, depending on the value of cancelled.
  • Either the button is rendered or not rendered, again depending on the value of cancelled.

The following code accomplishes this all:

import React, { useState } from 'react';

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

   return (
      <li>
         {cancelled ? <s>{item}</s> : item}
         {!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.

"I created Codeguage to save you from falling into the same learning conundrums that I fell into."

— Bilal Adnan, Founder of Codeguage