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

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

Welcome back. This is the second part of the tutorial series where we are learning React Native concepts as well as building a cross-platform mobile application that soon will be working with real time data. In the previous tutorial, we have seen how to setup React Native development environment utilizing Expo with other components such as modifying status bar, scrollable lists, input type, etc.

In this article, we will continue from the previous tutorial,

Adding Edit and Delete buttons

To add an edit and a delete button to each item in the todo list, we will again make use of TouchableOpacity. We will be putting everything in the previously built buttons and these two buttons will be placed adjacently like different columns in a row. The updated version of render() in Todolist.js.

render() {

    const {isEditing, isCompleted} = this.state;

    return (
         <View style={styles.container}>
        <View style={styles.rowContainer}>
        <TouchableOpacity onPress={this.toggleItem}>
        <View style={[styles.circle, isCompleted ? styles.completeCircle : styles.incompleteCircle]}>

        </View>
      </TouchableOpacity>
        <Text style={[styles.text, isCompleted ? styles.strikeText : styles.unstrikeText]}>
          Todo List will show here
        </Text>
        </View>
        {isEditing ? (
          <View style={styles.buttons}>
          <TouchableOpacity>
            <View style={styles.buttonContainer}>
            <Text style={styles.buttonText}>✅</Text>
            </View>
          </TouchableOpacity>
          </View>
        ) : (
          <View style={styles.buttons}>
          <TouchableOpacity>
            <View style={styles.buttonContainer}>
            <Text style={styles.buttonText}>✏</Text>
            </View>
          </TouchableOpacity>
          <TouchableOpacity>
            <View style={styles.buttonContainer}>
            <Text style={styles.buttonText}>❌</Text>
            </View>
          </TouchableOpacity>
          </View>
        )}
      </View>
    )
  }
};

We will now be using the first property defined in our local state and that isEditing to check whether the user is editing the item or not. If he/she is editing the item in the list, we will just show the checkmark button such that once he/she is done with editing, with just one touch the edited version will be saved. If the item is not in the editable mode, we will show two emojis, the pencil to render the editable mode and the red cross to delete the item from the list. Please note that we are now just building our user interface but will be adding the functionality later.

And there corresponding styles:

  container: {
    // add this new property
    justifyContent: 'space-between'
  },
rowContainer: {
    flexDirection: 'row',
    width: width / 2,
    alignItems: 'center',
    justifyContent: 'space-between'
  },
  buttons: {
    flexDirection: 'row',
  },
  buttonContainer: {
    marginVertical: 10,
    marginHorizontal: 10,
  }

Todo List will show here
To enable the functionality of changing to and fro in the editable mode, we create two custom functions add each one of them to the desired TouchableOpacity.

startEditing = () => {
    this.setState({
      isEditing: true
    });
  }

  finishEditing = () => {
    this.setState({
      isEditing: false
    });
  }

render() {
  // rest of the code

  // notice the onPressOut properties

<TouchableOpacity onPressOut={this.finishEditing}>
            <View style={styles.buttonContainer}>
            <Text style={styles.buttonText}>✅</Text>
            </View>
</TouchableOpacity>

/* ... */

<TouchableOpacity onPressOut={this.startEditing}>
            <View style={styles.buttonContainer}>
            <Text style={styles.buttonText}>✏</Text>
            </View>
</TouchableOpacity>
}

The onPressOut property is called as soon as the touch is released. You can see this functionality in action:
onPressOut property

Getting Text from the Props in TodoList

TodoList component will be receiving the text as a prop from App component such that we can utilize these items saving to the disk later. In App.js we will be passing a text prop.

<TodoList textValue={'TodoItem'} />

In Todolist.js, we will receive this prop inside our render() function.

<Text
  style={[styles.text, isCompleted ? styles.strikeText : styles.unstrikeText]}
>
  {textValue}
</Text>

Now, for editing purpose, we want to copy the text value in the state. When the user starts editing, we will trigger the startEditing() method. In that method, we will get value of a todo item from the props and update the newly created property in our state.

state = {
  todoValue: ''
};

startEditing = () => {
  const { textValue } = this.props;
  this.setState({
    isEditing: true,
    todoValue: textValue
  });
};

To able to edit the text of the todo item in the editing mode, we will introduce TextInput. Replace the previously defined Text code with:

controlInput = textValue => {
    this.setState({ todoValue: textValue });
  };

// inside render()
const { isEditing, isCompleted, todoValue } = this.state;


{
  isEditing ? (
    <TextInput
      value={todoValue}
      style={[
        styles.text,
        styles.input,
        isCompleted ? styles.strikeText : styles.unstrikeText
      ]}
      multiline={true}
      returnKeyType={'done'}
      onBlur={this.finishEditing}
      onChangeText={this.controlInput}
    />
  ) : (
    <Text
      style={[
        styles.text,
        isCompleted ? styles.strikeText : styles.unstrikeText
      ]}
    >
      {textValue}
    </Text>
  );
}

// styles

input: {
    marginVertical: 15,
    width: width / 2,
    paddingBottom: 5
  },

The TextInput triggers when the user clicks the pencil button. This is done by startEditing(). In TextInput, we are now having some new properties as attributes. Let’s see what they do:

• multiline: it enables the text Input to spread across multiple lines, if needed.
• onBlur: this enables a minute but useful beahvior with keyboards in mobile devices. If a user taps outside the keyboard, once he is done listing the edited item, the keyboard appeared on the screen will disappear.

At last, we have new custom function controlInput that takes textValue as argument coming as a prop and sets the todoValue in the state.
TouchableOpacity

AppLoading

AppLoading is a React component that allows Expo to keep the app loading screen open if it is the first and only component rendered in your app. When it is removed, the loading screen will disappear and your app will be visible. We can leverage this use case when our todo items are being loaded from the disk whenever we start the application in our devices. We will start by importing the component from Expo. We will also define a property in our local state called dataIsReady.

import { LinearGradient, AppLoading } from 'expo';

state = {
  // old state
    dataIsReady: false
  };

render() {
  const { newTodoItem, dataIsReady } = this.state;

  if (!dataIsReady) {
      return <AppLoading />;
    }
    return (
     // previous code
    );
}

Now, with the help of componenedDidMount() will add a custom function that will let the application know when all the data is loaded.

componentDidMount = () => {
  this.loadTodos();
};

loadTodos = () => {
  this.setState({ dataIsReady: true });
};

Adding an item to the list

In App.js, we must execute a function on TextInput to add the todo item from the input to the list.

addTodo = () => {
  if (this.state.newTodoItem !== '') {
    this.setState({
      // this will make the input field empty on clicking the done button
      newTodoItem: ''
    });
  }
};

;

We are going to associate multiple operations for each todo item in the list, such as adding, deleting, editing, etc, basically CRUD operations. We are going to use objects instead of an array to store these items. Operating CRUD operations on an Object is going to be easier in our case. We will be identifying each object through a unique id. In order to generate unique ids we are going to install a module called uuid.

In order proceed, first we need to run this command:

npm install

# after it runs successfully,
npm install --save uuid

The structure of each Todo model is going to be like this:

232390: {
  id: 232390,           // same id as the object
  text: 'New item',     // name of the todo item
  isCompleted: false,   // by default
}

We will be performing CRUD operations required for our application to work on an object instead of an Array. Another thing we have to implement before moving on to CRUD operations, is to add the new object of a Todo Item when created, should be added at the end of the list. We have to get the previous items added already in the object.

First, to create an ID we will be importing the uuid module we installed recently in our App.js.

// rest of imports
import uuidv1 from 'uuid/v1';

We will start by invoking this.setState that has access to a prevState object which will give us any Todo Item that has been previously added to our list. Inside its callback, we will first create a new ID using uuid and then create an object called newToDoObject which uses the ID as a variable for the name. Then, we create a new object called newState which uses the prevState object, clears the textInput for newTodoItem and finally adds our newToDoObject at the end of the other Todo items list. It might sound overwhelming that a lot is going on but try implementing the code, you will understand it better.

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
        }
      };

      return { ...newState };
    });
  }
};

Notice the todos object in the newState object we are creating. This will hold all our todo list items and for that to happen, we have to add it as an empty object in our local state.

state = {
  newTodoItem: '',
  dataIsReady: false,
  todos: {} // add this
};

// also access it in the render function
render() {
    const { newTodoItem, dataIsReady, todos } = this.state;
    // .. rest of the code

In our ScrollView, we are just sending a default todo item that is not coming from our newly defined todos in the local state of our application.

<ScrollView contentContainerStyle={styles.listContainer}>
  {Object.values(todos).map(todo => <TodoList key={todo.id} {...todo} />)}
</ScrollView>

We are converting our todos object into an array by getting its values and then using map to traverse that array to get the todo items. We will start by adding a new item to the list.
add new item

item1

If you are wondering how the todo item object looks, here it is:
todo item object
Even though we are not saving these items in the local storage, we do have working prototype of our application.

Adding PropTypes

To follow one of the React’s best practices, we will add PropTypes to TodoList component. Proptypes are added to those components who are receiving props from a parent component. How do we access them? This time, we do not have to install the package. Expo already includes the prop-types module with it. We just have to import it and start defining them.

// rest of imports
import PropTypes from 'prop-types';

// in our class before we define state
static propTypes = {
    textValue: PropTypes.string.isRequired,
    isCompleted: PropTypes.bool.isRequired
  };

We will also remove isCompleted from our state that we previously defined.

state = {
  isEditing: false,
  // isCompleted: false,  // remove this
  todoValue: ''
};

For brevity, we will be adding the constructor function to our class TodoList such that in the rest of the component we don’t need props and every-time we are getting text from the parent component, we have to initialize it to todoValue.

class TodoList extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isEditing: false,
      todoValue: props.textValue
    };
  }
  // rest of the code
}

We will also remove the reference of todoValue from startEditing method since we are now getting these values as soon as the component is rendered.

startEditing = () => {
  this.setState({
    isEditing: true
  });
};

The second part of our tutorial series comes to an end here. I hope you enjoyed while building the application. There was a lot more to do in our app as compared to the previous one. In the last part of the series we will be working on saving and manipulating (implementing CRUD operations) on real time data in a device’s storage.

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 -