Skip to content

Possible Improvements

There are 2 things we can improve in our current solution.

First, to make things more readable and rescalable, it would be good if we extract list item implementation from List component in a completely new component called ListItem.tsx and we can use it inside our List component and pass props that are needed to render everything properly.

import { Todo } from "../store/reducers/todo";

interface ListItemInterface {
    item: Todo;
    removeItemFromList: Function;
    toggleItemDone: Function;
}

function ListItem(props: ListItemInterface) {
    return (
        <li className="flex items-center p-2">
            <input
                onChange={() => props.toggleItemDone(props.item.id)}
                type="checkbox"
                className="h-4 w-4 mr-2 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
                checked={props.item.is_done}
            />
            <p
                className={
                    "w-full" + (props.item.is_done ? " line-through" : "")
                }
            >
                {props.item.title}
            </p>
            <button
                onClick={() => props.removeItemFromList(props.item.id)}
                type="button"
                className="w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-2 py-0.5 bg-white text-base font-medium text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-offset-2 focus:ring-red-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
            >
                Remove
            </button>
        </li>
    );
}

export default ListItem;

When we created our ListItem component and extracted logic from List component, we need to update our List component to use ListItem component.

import { Todo } from "../store/reducers/todo";
import ListItem from "./ListItem";

interface ListInterface {
    list: Todo[];
    removeItemFromList: Function;
    toggleItemDone: Function;
}

function List(props: ListInterface) {
    return (
        <ul className="p-8 pt-0 divide-y">
            {props.list.length > 0 &&
                props.list.map((todo_item: Todo) => (
                    <ListItem
                        key={todo_item.id}
                        item={todo_item}
                        removeItemFromList={props.removeItemFromList}
                        toggleItemDone={props.toggleItemDone}
                    />
                ))}
            {props.list.length < 1 && (
                <p>Your todo list is empty, please add some todo.</p>
            )}
        </ul>
    );
}

export default List;

At the end, everything should look and work the same, but we just want to make our code more readable and scalable.


One more thing that we can do a bit better is to remove input value whenever we add an item, so after adding an item input field gets cleared.

import { useDispatch, useSelector } from "react-redux";
import ListHeader from "../components/ListHeader";
import List from "../components/List";
import { addItem, markItemAsDone, removeItem } from "../store/actions/todo";
import { useState } from "react";
import { RootState } from "../store/reducers";

function TodoList() {
    const dispatch = useDispatch();
    const [title, setTitle] = useState<string>("");

    const todo: any = useSelector((state: RootState) => state.todo);

    const addItemToList = () => {
        if (!title) return;

        dispatch(addItem(title));
        setTitle('');
    };

    const removeItemFromList = (item_id: number) => {
        dispatch(removeItem(item_id));
    };

    const toggleItemDone = (item_id: number) => {
        dispatch(markItemAsDone(item_id));
    };

    return (
        <div className="flex justify-center items-center h-screen">
            <div className="shadow-lg rounded-lg w-[500px]">
                <ListHeader
                    addItemToList={addItemToList}
                    title={title}
                    setTitle={setTitle}
                />
                <List
                    list={todo.todo_list}
                    removeItemFromList={removeItemFromList}
                    toggleItemDone={toggleItemDone}
                />
            </div>
        </div>
    );
}

export default TodoList;

Last update: March 28, 2022