Rendering List from Store¶
Basically since we never rendered a list from our store, and as we mentioned before, our list is prerendered in the List.tsx
component.
Lets get the todo list from our Redux store using useSelector
hook and pass it as prop to List.tsx
component so we can render items from the array.
import { useDispatch, useSelector } from "react-redux";
import ListHeader from "../components/ListHeader";
import List from "../components/List";
import { addItem } 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));
};
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} />
</div>
</div>
);
}
export default TodoList;
This will error out since again list
prop is not defined in our List.tsx
. Lets define the interface for our List.tsx
and allow it to accept list
of Todo
types.
import { Todo } from "../store/reducers/todo";
interface ListInterface {
list: Todo[];
}
function List(props: ListInterface) {
return (
<ul className="p-8 pt-0 divide-y">
<li className="flex items-center p-2">
<input type="checkbox" className="h-4 w-4 mr-2 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" />
<p className="w-full">Todo 1</p>
<button 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>
<li className="flex items-center p-2">
<input type="checkbox" className="h-4 w-4 mr-2 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" />
<p className="w-full">Todo 2</p>
<button 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>
<li className="flex items-center p-2">
<input type="checkbox" className="h-4 w-4 mr-2 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded" />
<p className="w-full">Todo 3</p>
<button 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>
</ul>
);
}
export default List;
Everything still looks the same? That is because we did not use our list and did not display values from it. Lets do it now.
import { Todo } from "../store/reducers/todo";
interface ListInterface {
list: Todo[];
}
function List(props: ListInterface) {
return (
<ul className="p-8 pt-0 divide-y">
{props.list.map((todo_item: Todo) => (
<li key={todo_item.id} className="flex items-center p-2">
<input
type="checkbox"
className="h-4 w-4 mr-2 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
checked={todo_item.is_done}
/>
<p className="w-full">{todo_item.title}</p>
<button
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>
))}
</ul>
);
}
export default List;
Awesome! If you now add a new item, it displays as expected.
When the list is empty at the load, we do not see anything. Lets add a part to check if list is empty and display the message to the user.
import { Todo } from "../store/reducers/todo";
interface ListInterface {
list: Todo[];
}
function List(props: ListInterface) {
return (
<ul className="p-8 pt-0 divide-y">
{props.list.length > 0 && props.list.map((todo_item: Todo) => (
<li key={todo_item.id} className="flex items-center p-2">
<input
type="checkbox"
className="h-4 w-4 mr-2 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
checked={todo_item.is_done}
/>
<p className="w-full">{todo_item.title}</p>
<button
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>
))}
{props.list.length < 1 && (
<p>Your todo list is empty, please add some todo.</p>
)}
</ul>
);
}
export default List;
Cool, we have our simple todo list working. However, you notice that clicking our checkbox to mark it as done or Remove button does nothing.
We will cover that in the next part.