Mobile ProgrammingLearn How to Build a React Native ToDo Application – Part 3

Learn How to Build a React Native ToDo Application – Part 3

How to Build a React Native Todo Application

This is the last part of our tutorial series. We have already worked on building the UI of our application and major functionality. All we have to do is to connect those functions to work with real-time data. By the end of this article, we will have a complete application working that can run either on iOS and Android. Let us continue where we left in the second part.

Deleting a Todo Item

To delete an item from the todo list object, we have to get the id of the item from the state. In App.js we will add a new method deleteTodo.

deleteTodo = id => {
  this.setState(prevState => {
    const todos = prevState.todos;
    delete todos[id];
    const newSate = {
      ...prevState,
      ...todos
    };
    return { ...newSate };
  });
};

To see it in action, we are going to pass it as a prop to our TodoList component. We will also add the id of an individual todo item, since we are going to use this id to delete the item from the list.

// in app.js
<TodoList key={todo.id} {...todo} deleteTodo={this.deleteTodo} />

// in TodoList.js, add this where we define other propsTypes
deleteTodo: PropTypes.func.isRequired,
id: PropTypes.string.isRequired

// also destructure props in render function
const { textValue, id, deleteTodo } = this.props;

We will now add this new prop on our ❌ which is our delete button by using onPressOut method on our already defined TouchableOpacity element.

<TouchableOpacity onPressOut={() => deleteTodo(id)}>

deleteTodo
That’s it! We can now delete an item from our Todo List.

Complete and InComplete Methods

We are now going to do functionality reading and writing Complete or InComplete Todo list item by defining two new methods in our App.js: completeTodo() and inCompleteTodo. We will be using the id of each individual todo item for the reference and to implement necessary operations. You should now clearly notice a pattern we are using in all different CRUD functions of accessing the previous state and returning new state when the operation is complete.

inCompleteTodo = id => {
  this.setState(prevState => {
    const newState = {
      ...prevState,
      todos: {
        ...prevState.todos,
        [id]: {
          ...prevState.todos[id],
          isCompleted: false
        }
      }
    };
    return { ...newState };
  });
};

completeTodo = id => {
  this.setState(prevState => {
    const newState = {
      ...prevState,
      todos: {
        ...prevState.todos,
        [id]: {
          ...prevState.todos[id],
          isCompleted: true
        }
      }
    };
    return { ...newState };
  });
};

These two functions will help us utilize the functionality of checking or unchecking an item in the todo list when saving them in the disk. So far, our UI is implementing the same functionality but it alone cannot access the disk. We will pass both these functions as props to TodoList.

// in Todolist, add their proptypes
inCompleteTodo: PropTypes.func.isRequired,
completeTodo: PropTypes.func.isRequired

All we have to edit is one function that is implementing the same functionality in the UI: toggleItem.

toggleItem = () => {
  const { isCompleted, inCompleteTodo, completeTodo, id } = this.props;
  if (isCompleted) {
    inCompleteTodo(id);
  } else {
    completeTodo(id);
  }
};

// in render()

const { isEditing, todoValue } = this.state;
const { textValue, id, deleteTodo, isCompleted } = this.props;

toggleItem
The difference in value of isCompleted is coming from App.js.

Updating a Todo Item

The updateTodo method will be similar to the methods we previously defined. The only difference is that we are going to pass textValue along with id as the argument since the purpose of this method is to update the text of a particular todo item.

updateTodo = (id, textValue) => {
  this.setState(prevState => {
    const newState = {
      ...prevState,
      todos: {
        ...prevState.todos,
        [id]: {
          ...prevState.todos[id],
          textValue: textValue
        }
      }
    };
    return { ...newState };
  });
};

We will then pass it as a prop in TodoList and define its proptype.

// in App.js

<TodoList
  key={todo.id}
  {...todo}
  deleteTodo={this.deleteTodo}
  inCompleteTodo={this.inCompleteTodo}
  completeTodo={this.completeTodo}
  updateTodo={this.updateTodo} // add this
/>;

// in TodoList.js

updateTodo: PropTypes.func.isRequired;

We will not edit finishEditing method to update the text value of an existing todo item.

finishEditing = () => {
  const { todoValue } = this.state;
  const { id, updateTodo } = this.props;
  updateTodo(id, todoValue);
  this.setState({
    isEditing: false
  });
};

Saving to the Disk using AsyncStorage

According to the React Native documentations, AsyncStorage is:

a simple, unencrypted, asynchronous, persistent, key-value storage system that is global to the app. It should be used instead of LocalStorage.

On iOS, AsyncStorage is backed by native code that stores small values in a serialized dictionary and larger values in separate files. On Android, AsyncStorage will use either RocksDB or SQLite based on what is available.

The CRUD operations we have previously created will be modified using AsyncStorage such that our application can perform these operations with real-time data on a device.

Instead of modifying our previous operations by injecting a save functionality within the method, we will create a new method without writing the code in every function. Let us first implement the functionality of saving a state inside a single method that can be reused throughout the App component.

// import AsyncStorage from 'react-native'

saveTodos = newToDos => {
  const saveTodos = AsyncStorage.setItem('todos', newToDos);
};

// addition to below methods
addTodo = () => {
  const { newTodoItem } = this.state;

  if (newTodoItem !== '') {
    this.setState(prevState => {
      const ID = uuidv1();
      const newToDoObject = {
        [ID]: {
          id: ID,
          isCompleted: false,
          textValue: newTodoItem,
          createdAt: Date.now()
        }
      };
      const newState = {
        ...prevState,
        newTodoItem: '',
        todos: {
          ...prevState.todos,
          ...newToDoObject
        }
      };
      this.saveTodos(newState.todos); // add this
      return { ...newState };
    });
  }
};

deleteTodo = id => {
  this.setState(prevState => {
    const todos = prevState.todos;
    delete todos[id];
    const newSate = {
      ...prevState,
      ...todos
    };
    this.saveTodos(newState.todos); // add this
    return { ...newSate };
  });
};

inCompleteTodo = id => {
  this.setState(prevState => {
    const newState = {
      ...prevState,
      todos: {
        ...prevState.todos,
        [id]: {
          ...prevState.todos[id],
          isCompleted: false
        }
      }
    };
    this.saveTodos(newState.todos); // add this
    return { ...newState };
  });
};

completeTodo = id => {
  this.setState(prevState => {
    const newState = {
      ...prevState,
      todos: {
        ...prevState.todos,
        [id]: {
          ...prevState.todos[id],
          isCompleted: true
        }
      }
    };
    this.saveTodos(newState.todos); // add this
    return { ...newState };
  });
};

updateTodo = (id, textValue) => {
  this.setState(prevState => {
    const newState = {
      ...prevState,
      todos: {
        ...prevState.todos,
        [id]: {
          ...prevState.todos[id],
          textValue: textValue
        }
      }
    };
    this.saveTodos(newState.todos); // add this
    return { ...newState };
  });
};

Right now, if you try to run your application and add an item to the list, the whole application will crash. The reason behind this is AsyncStorage can only save data if they are strings. It cannot store objects. So we have to convert our object into a string. Let us now modify the line where we are saving our todo items.

const saveTodos = AsyncStorage.setItem('todos', JSON.stringify(newToDos));

setItem() function from AsyncStorage is similar to any key-value paired database. The first item todos is the key, and newToDos is going to be the value, in our case the todo list items. Our application is working just as before. The only difference now is,we are saving the data in real-time.
AsyncStorage
To verify the saved data on the device, restart the application, whereas the data will not appear. Why? Because we are not loading the data from the disk.

Loading Real-Time Data

Our loadTodos function is an asynchronous one. We have to wait till the application is done reading data from the device’s storage. Let us modify this function in order to behave the way we want.

loadTodos = async () => {
  try {
    const getTodos = await AsyncStorage.getItem('todos');
    const parsedTodos = JSON.parse(getTodos);
    this.setState({ dataIsReady: true, todos: parsedTodos || {} });
  } catch (err) {
    console.log(err);
  }
};

loadTodos function

We are going to use async-await for the loadTodos to behave asynchronously. We are then getting the todos object still in string format from the device storage using getItem() function from AsyncStorage. We are converting the todos getting from AsyncStorage into an object by using JSON.parese(). At last, we are going to update the local state of our application. If you restart your application you can clearly see that the items stored in the previous step are now being displayed on the screen.

This completes our tutorial for building a React Native Application from scratch using Expo. You can add more functionality like, reversing the order of items in the todo list such that whenever a new item is being added, it gets to the top of the list rather than the bottom. Add a splash screen along with the app icon and get it ready for the app store. The possibilities of enhancing this application model are endless.

I am sure with this tutorial, you are now familiar with building a React Application using Expo and working with device local storage using AsyncStorage.

1 COMMENT

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Exclusive content

- Advertisement -

Latest article

21,501FansLike
4,106FollowersFollow
106,000SubscribersSubscribe

More article

- Advertisement -