CodeBlanks Logo Icon CodeBlanks

Completing tasks

Add a completion toggle.

Completing tasks

Now we add the ability to complete tasks. When a task is tapped its done field becomes true and it gets struck through. The toggleTask function takes an id and flips the done field of the matching task in the tasks array. .map() rebuilds the array.

Example: const toggleTask = (id) => setTasks(tasks.map((t) => t.id === id ? { ...t, done: !t.done } : t)). The ternary flips done on the matching task and leaves the others as they are. Later inside renderItem a TouchableOpacity adds the tap and textDecorationLine: 'line-through' adds the strikethrough look.

Practice Add the toggle

Make two changes. (1) Below addTask add the toggleTask function: const toggleTask = (id) => setTasks(tasks.map((t) => t.id === id ? { ...t, done: !t.done } : t)). (2) Update renderItem to <TouchableOpacity onPress={() => toggleTask(item.id)}><Text style={{ textDecorationLine: item.done ? 'line-through' : 'none' }}>{item.text}</Text></TouchableOpacity>.

Starter code

App.js
import React from 'react';
import { SafeAreaView, View, Text, TextInput, Button, FlatList, TouchableOpacity, StyleSheet } from 'react-native';

export default function App() {
  const [task, setTask] = React.useState('');
  const [tasks, setTasks] = React.useState([]);
  const addTask = () => {
    setTasks([...tasks, { id: Date.now().toString(), text: task, done: false }]);
    setTask('');
  };
  // Add the toggleTask function here
  return (
    <SafeAreaView style={styles.container}>
      <Text>To-do</Text>
      <TextInput placeholder="New task" value={task} onChangeText={setTask} />
      <Button title="Add" onPress={addTask} />
      <FlatList
        data={tasks}
        renderItem={({ item }) => <Text>{item.text}</Text>}
        keyExtractor={(item) => item.id}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 16 }
});

Sample solution

App.js
import React from 'react';
import { SafeAreaView, View, Text, TextInput, Button, FlatList, TouchableOpacity, StyleSheet } from 'react-native';

export default function App() {
  const [task, setTask] = React.useState('');
  const [tasks, setTasks] = React.useState([]);
  const addTask = () => {
    setTasks([...tasks, { id: Date.now().toString(), text: task, done: false }]);
    setTask('');
  };
  const toggleTask = (id) => setTasks(tasks.map((t) => (t.id === id ? { ...t, done: !t.done } : t)));
  return (
    <SafeAreaView style={styles.container}>
      <Text>To-do</Text>
      <TextInput placeholder="New task" value={task} onChangeText={setTask} />
      <Button title="Add" onPress={addTask} />
      <FlatList
        data={tasks}
        renderItem={({ item }) => (
          <TouchableOpacity onPress={() => toggleTask(item.id)}>
            <Text style={{ textDecorationLine: item.done ? 'line-through' : 'none' }}>{item.text}</Text>
          </TouchableOpacity>
        )}
        keyExtractor={(item) => item.id}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 16 }
});

The strikethrough look

Completed tasks get a strikethrough; this visual cue tells the user the task is done. In React Native you use textDecorationLine: 'line-through' for this. When item.done is true the line appears, when false 'none' removes it.

Example: <Text style={{ textDecorationLine: item.done ? 'line-through' : 'none' }}>{item.text}</Text>. The ternary gives the text a dynamic style. This pattern fits React Native's declarative nature: styles depend on state, and the look updates automatically when state changes.

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Match each field to its meaning

Term Meaning
id Unique key
text Task text
done Completion state
toggleTask Flips done

2 What does toggleTask do?

App.js
const toggleTask = (id) =>
  setTasks(tasks.map((t) =>
    t.id === id ? { ...t, done: !t.done } : t
  ));
  • Deletes the task from the array
  • Flips the done field of the matching task Correct answer
  • Adds a new task
  • Marks all tasks done

Explanation: toggleTask flips the done field of the matching task.

3 Write the toggleTask body

Question
const toggleTask = (id) => setTasks(tasks.map((t) => t.id === id ? { ...t,____: !t.done } : t));
Answer
const toggleTask = (id) => setTasks(tasks.map((t) => t.id === id ? { ...t,done: !t.done } : t));

Explanation: The done field is flipped; the task toggles completion.

All lessons in Beginner