Skip to content

Adding New Todo Item to Backend

To let user add items to the database, we will first modify and existing action addItem that will call our POST endpoint on backend, get the data and dispatch action to update our store and add item to the list.

export function addItem(title: string) {
    return async (dispatch: any) => {
        try {
            const addTodoItem: any = await fetch('http://localhost:3001/todo', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    title,
                })
            });

            const body = await addTodoItem.json();

            if (addTodoItem.status !== 200) {
                return dispatch({ type: 'ADD_ITEM_ERROR', message: body.message });
            }

            return dispatch({ type: 'ADD_ITEM_SUCCESS', item: body.todo });
        } catch (error) {
            return dispatch({ type: 'ADD_ITEM_ERROR', message: 'Something went wrong.' });
        }
    };
}

export function removeItem(item_id: number) {
    return {
        type: 'REMOVE_ITEM',
        item_id
    }
}

export function markItemAsDone(item_id: number) {
    return {
        type: 'MARK_ITEM_DONE',
        item_id
    }
}

export function getTodos() {
    return async (dispatch: any) => {
        try {
            const todoList: any = await fetch('http://localhost:3001/todos', {
                method: 'GET'
            });

            const body = await todoList.json();

            if (todoList.status !== 200) {
                return dispatch({ type: 'GET_TODOS_ERROR', message: body.message });
            }

            return dispatch({ type: 'GET_TODOS_SUCCESS', items: body.data });
        } catch (error) {
            return dispatch({ type: 'GET_TODOS_ERROR', message: 'Something went wrong.' });
        }
    };
}
We will now have 2 action types for adding a new item ADD_ITEM_SUCCESS that will dispatch action to reducer with todo item that was added to the database and ADD_ITEM_ERROR that will be called in case something went wrong and pass error message to be stored in reducer.

If we for example send title "Todo 2", body value should look like

{
    "todo": {
        "title": "Todo 2",
        "is_done": false,
        "_id": "85f358e91af25f1b4ad24bdd",
        "__v": 0
    }
}

Just like for getting todo items, we make sure that if anything else is wrong (like typos), we dispatch ADD_ITEM_ERROR with default "Something went wrong." message in catch block.


Now we have 2 new action types to handle in our reducer ADD_ITEM_SUCCESS and ADD_ITEM_ERROR.

export interface Todo {
    id: number;
    title: string;
    is_done: boolean;
}

interface TodoReducerInterface {
    todo_list: Todo[],
    error: '',
}

const INITIAL_STATE: TodoReducerInterface = {
    todo_list: [],
    error: '',
}

const todoReducer = (state = INITIAL_STATE, action: any) => {
    switch (action.type) {
        case "ADD_ITEM_SUCCESS":
            return {
                ...state,
                todo_list: [
                    ...state.todo_list,
                    action.item
                ]
            }
        case "ADD_ITEM_ERROR":
            return {
                ...state,
                error: action.message,
            }
        case "REMOVE_ITEM":
            const removeItemIndex = state.todo_list.findIndex((item: any) => item.id === action.item_id);

            if (removeItemIndex < 0) {
                return state;
            }

            return {
                ...state,
                todo_list: [
                    ...state.todo_list.slice(0, removeItemIndex),
                    ...state.todo_list.slice(removeItemIndex + 1),
                ]
            }
        case "MARK_ITEM_DONE":
            const doneItemIndex: number = state.todo_list.findIndex((item: any) => item.id === action.item_id);

            if (doneItemIndex < 0) {
                return state;
            }

            return {
                ...state,
                todo_list: [
                    ...state.todo_list.slice(0, doneItemIndex),
                    {
                        ...state.todo_list[doneItemIndex],
                        is_done: !state.todo_list[doneItemIndex].is_done
                    },
                    ...state.todo_list.slice(doneItemIndex + 1),
                ]
            }
        case "GET_TODOS_SUCCESS":
            return {
                ...state,
                todo_list: action.items,
                error: '',
            }
        case "GET_TODOS_ERROR":
            return {
                ...state,
                todo_list: [],
                error: action.message
            }
        default:
            return state;
    }
};

export default todoReducer;

Now, we can try to add a new todo item through application with title "Todo 2". Inpunt "Todo 2" inside input box and hit "+ Add" button.

If you did everything as you should, a new item will be added to database and will appear in the list.

React App Todo New Item
React App Todo New Item

We successfully added a new item to database and displayed it. In the next part we will make users able to remove an existing item from the database with provided id and automatically remove it from the list.