Adding Todo Item to Store¶
The next step would be to handle onClick actions in our application and dispatch actions based on what button/checkbox was clicked/checked.
We will first handle event of adding new items in the list when user clicks "+ Add" button.
Lets do that in TodoList.tsx
container and then we will pass that function as prop to our ListHeader
component.
import { useDispatch } from "react-redux";
import ListHeader from "../components/ListHeader";
import List from "../components/List";
import { addItem } from "../store/actions/todo";
function TodoList() {
const dispatch = useDispatch();
const addItemToList = () => {
dispatch(addItem());
};
return (
<div className="flex justify-center items-center h-screen">
<div className="shadow-lg rounded-lg w-[500px]">
<ListHeader
addItemToList={addItemToList}
/>
<List />
</div>
</div>
);
}
export default TodoList;
This will error out since we are passing addItemToList
prop to ListHeader component but it is not defined there.
Lets define that property and tell our ListHeader
component that it should expect addItemToList
to be defined when it is initialized.
Also, since we use Typescript, we will define interface for props, so we know what properties ListHeader should expect.
interface ListHeaderInterface {
addItemToList: React.MouseEventHandler<HTMLButtonElement> | undefined;
}
function ListHeader(props: ListHeaderInterface) {
return (
<div className="flex items-center p-8">
<form className="basis-full group relative">
<input
className="focus:ring-2 focus:ring-blue-500
focus:outline-none w-full text-sm leading-6
text-gray-900 placeholder-gray-400
rounded-md py-2 px-4 ring-1 ring-gray-200 shadow-sm"
type="text"
aria-label="Add to list"
placeholder="Add to list"
/>
</form>
<button
onClick={props.addItemToList}
type="button"
className="animate-bounce whitespace-nowrap bg-indigo-600
text-white text-sm leading-6 font-medium py-2 px-4 ml-2
rounded-lg transition ease-in-out delay-0 bg-blue-500
hover:-translate-y-1 hover:bg-indigo-500 duration-300"
>
+ Add
</button>
</div>
);
}
export default ListHeader;
Okay, we passed the addItemToList
prop, and command our button to call that function on every click.
But our TodoList.tsx
still shows an error? Right?
Because our addItem
action expects title
to be passed in order to add a new todo item in the list.
Now we need to modify our TodoList.tsx
so it saves whatever user types in the input field, so it can be sent to our action and added to state.
For that we will use React Hook useState
and event onChange
on our input. This will basically populate our declared variabled in useState
whenever user types something in the input field.
import { useDispatch } from "react-redux";
import ListHeader from "../components/ListHeader";
import List from "../components/List";
import { addItem } from "../store/actions/todo";
import { useState } from "react";
function TodoList() {
const dispatch = useDispatch();
const [title, setTitle] = useState<string>("");
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 />
</div>
</div>
);
}
export default TodoList;
We passed the title value to addItem
action whenever button is clicked.
Also did some checks that value is not empty, since we do not want todos with no title, right?
We also passed getter title
and setter setTitle
to our ListHeader
as props in order to be able to handle onChange
event on input properly and to display the current value in the input field.
Lets modify our ListHeader
components interface and props so we get this working.
interface ListHeaderInterface {
addItemToList: React.MouseEventHandler<HTMLButtonElement> | undefined;
title: string;
setTitle: Function;
}
function ListHeader(props: ListHeaderInterface) {
return (
<div className="flex items-center p-8">
<form className="basis-full group relative">
<input
value={props.title}
onChange={e => props.setTitle(e.target.value)}
className="focus:ring-2 focus:ring-blue-500
focus:outline-none w-full text-sm leading-6
text-gray-900 placeholder-gray-400
rounded-md py-2 px-4 ring-1 ring-gray-200 shadow-sm"
type="text"
aria-label="Add to list"
placeholder="Add to list"
/>
</form>
<button
onClick={props.addItemToList}
type="button"
className="animate-bounce whitespace-nowrap bg-indigo-600
text-white text-sm leading-6 font-medium py-2 px-4 ml-2
rounded-lg transition ease-in-out delay-0 bg-blue-500
hover:-translate-y-1 hover:bg-indigo-500 duration-300"
>
+ Add
</button>
</div>
);
}
export default ListHeader;
Now, when we input something into our input field and click Add button, nothing happens. Why is that?
It works but we do not see the changes, because we never rendered our list from the store, lets do that in the next part.