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;