Skip to content

项目1:简易待办清单(Todo App)- 增删改查功能

1. 功能概述

在本章节中,我们将实现待办清单应用的核心功能:增删改查(CRUD)。这些功能包括:

  • 创建(Create):添加新的待办事项
  • 读取(Read):查看所有待办事项
  • 更新(Update):修改待办事项的状态(完成/未完成)
  • 删除(Delete):删除不需要的待办事项

我们已经在项目结构搭建中实现了基本的增删改查功能,现在将进一步完善和优化这些功能。

2. 增强添加待办事项功能

2.1 添加验证

为了提高用户体验,我们需要添加输入验证,确保用户不能添加空的待办事项:

javascript
// components/TodoInput.js
import React, { useState } from 'react';
import { View, TextInput, TouchableOpacity, Text, StyleSheet, Alert } from 'react-native';

const TodoInput = ({ onAddTodo }) => {
  const [text, setText] = useState('');

  const handleAdd = () => {
    const trimmedText = text.trim();
    if (trimmedText) {
      onAddTodo(trimmedText);
      setText('');
    } else {
      Alert.alert('提示', '请输入待办事项内容');
    }
  };

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="添加待办事项..."
        value={text}
        onChangeText={setText}
        onSubmitEditing={handleAdd}
        returnKeyType="done"
      />
      <TouchableOpacity style={styles.button} onPress={handleAdd}>
        <Text style={styles.buttonText}>添加</Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    marginBottom: 20,
  },
  input: {
    flex: 1,
    borderWidth: 1,
    borderColor: '#ddd',
    padding: 10,
    borderRadius: 5,
    backgroundColor: '#fff',
    marginRight: 10,
  },
  button: {
    backgroundColor: '#007AFF',
    padding: 10,
    borderRadius: 5,
    justifyContent: 'center',
    alignItems: 'center',
  },
  buttonText: {
    color: '#fff',
    fontWeight: 'bold',
  },
});

export default TodoInput;

2.2 添加动画效果

为了让添加待办事项的过程更加流畅,我们可以添加动画效果:

javascript
// components/TodoInput.js
import React, { useState, useRef } from 'react';
import { View, TextInput, TouchableOpacity, Text, StyleSheet, Alert, Animated } from 'react-native';

const TodoInput = ({ onAddTodo }) => {
  const [text, setText] = useState('');
  const buttonScale = useRef(new Animated.Value(1)).current;

  const handleAdd = () => {
    const trimmedText = text.trim();
    if (trimmedText) {
      // 添加按钮动画
      Animated.sequence([
        Animated.timing(buttonScale, {
          toValue: 0.9,
          duration: 100,
          useNativeDriver: true,
        }),
        Animated.timing(buttonScale, {
          toValue: 1,
          duration: 100,
          useNativeDriver: true,
        }),
      ]).start();

      onAddTodo(trimmedText);
      setText('');
    } else {
      Alert.alert('提示', '请输入待办事项内容');
    }
  };

  return (
    <View style={styles.container}>
      <TextInput
        style={styles.input}
        placeholder="添加待办事项..."
        value={text}
        onChangeText={setText}
        onSubmitEditing={handleAdd}
        returnKeyType="done"
      />
      <Animated.View style={{ transform: [{ scale: buttonScale }] }}>
        <TouchableOpacity style={styles.button} onPress={handleAdd}>
          <Text style={styles.buttonText}>添加</Text>
        </TouchableOpacity>
      </Animated.View>
    </View>
  );
};

// 样式保持不变

export default TodoInput;

3. 增强更新待办事项功能

3.1 添加动画效果

为了让更新待办事项状态的过程更加直观,我们可以添加动画效果:

javascript
// components/TodoItem.js
import React, { useRef } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, Animated } from 'react-native';

const TodoItem = ({ todo, onPress, onDelete }) => {
  const opacity = useRef(new Animated.Value(1)).current;
  const scale = useRef(new Animated.Value(1)).current;

  const handlePress = () => {
    // 添加点击动画
    Animated.sequence([
      Animated.timing(scale, {
        toValue: 0.95,
        duration: 100,
        useNativeDriver: true,
      }),
      Animated.timing(scale, {
        toValue: 1,
        duration: 100,
        useNativeDriver: true,
      }),
    ]).start();

    onPress(todo.id);
  };

  return (
    <Animated.View style={{ transform: [{ scale }], opacity }}>
      <TouchableOpacity style={styles.container} onPress={handlePress}>
        <View style={[styles.checkbox, todo.completed && styles.checkboxCompleted]}>
          {todo.completed && <Text style={styles.checkmark}>✓</Text>}
        </View>
        <Text style={[styles.text, todo.completed && styles.textCompleted]}>
          {todo.text}
        </Text>
        <TouchableOpacity onPress={() => onDelete(todo.id)} style={styles.deleteButton}>
          <Text style={styles.deleteText}>×</Text>
        </TouchableOpacity>
      </TouchableOpacity>
    </Animated.View>
  );
};

// 样式保持不变

export default TodoItem;

3.2 添加编辑功能

现在,我们将添加编辑待办事项的功能:

javascript
// components/TodoItem.js
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, TextInput, Alert } from 'react-native';

const TodoItem = ({ todo, onPress, onDelete, onEdit }) => {
  const [isEditing, setIsEditing] = useState(false);
  const [editText, setEditText] = useState(todo.text);

  const handleEdit = () => {
    const trimmedText = editText.trim();
    if (trimmedText) {
      onEdit(todo.id, trimmedText);
      setIsEditing(false);
    } else {
      Alert.alert('提示', '待办事项内容不能为空');
    }
  };

  return (
    <View style={styles.container}>
      <TouchableOpacity 
        style={styles.checkboxContainer} 
        onPress={() => onPress(todo.id)}
      >
        <View style={[styles.checkbox, todo.completed && styles.checkboxCompleted]}>
          {todo.completed && <Text style={styles.checkmark}>✓</Text>}
        </View>
      </TouchableOpacity>

      {isEditing ? (
        <TextInput
          style={[styles.input, todo.completed && styles.textCompleted]}
          value={editText}
          onChangeText={setEditText}
          onSubmitEditing={handleEdit}
          returnKeyType="done"
          autoFocus
        />
      ) : (
        <TouchableOpacity 
          style={styles.textContainer} 
          onPress={() => setIsEditing(true)}
        >
          <Text style={[styles.text, todo.completed && styles.textCompleted]}>
            {todo.text}
          </Text>
        </TouchableOpacity>
      )}

      <TouchableOpacity onPress={() => onDelete(todo.id)} style={styles.deleteButton}>
        <Text style={styles.deleteText}>×</Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    alignItems: 'center',
    backgroundColor: '#fff',
    padding: 15,
    borderRadius: 5,
    marginBottom: 10,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.1,
    shadowRadius: 3.84,
    elevation: 5,
  },
  checkboxContainer: {
    marginRight: 10,
  },
  checkbox: {
    width: 20,
    height: 20,
    borderWidth: 2,
    borderColor: '#007AFF',
    borderRadius: 4,
    justifyContent: 'center',
    alignItems: 'center',
  },
  checkboxCompleted: {
    backgroundColor: '#007AFF',
  },
  checkmark: {
    color: '#fff',
    fontWeight: 'bold',
  },
  textContainer: {
    flex: 1,
  },
  text: {
    fontSize: 16,
  },
  textCompleted: {
    textDecorationLine: 'line-through',
    color: '#999',
  },
  input: {
    flex: 1,
    fontSize: 16,
    padding: 5,
    borderBottomWidth: 1,
    borderBottomColor: '#007AFF',
  },
  deleteButton: {
    padding: 5,
  },
  deleteText: {
    fontSize: 24,
    color: '#ff3b30',
    fontWeight: 'bold',
  },
});

export default TodoItem;

4. 增强删除待办事项功能

4.1 添加确认对话框

为了防止用户误操作,我们可以添加删除确认对话框:

javascript
// screens/TodoListScreen.js
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, StyleSheet, Alert } from 'react-native';
import TodoItem from '../components/TodoItem';
import TodoInput from '../components/TodoInput';
import { storage } from '../utils/storage';
import { globalStyles } from '../styles/global';

const TodoListScreen = () => {
  const [todos, setTodos] = useState([]);

  // 加载待办事项
  useEffect(() => {
    loadTodos();
  }, []);

  const loadTodos = async () => {
    const savedTodos = await storage.getTodos();
    setTodos(savedTodos);
  };

  const saveTodos = async (newTodos) => {
    setTodos(newTodos);
    await storage.saveTodos(newTodos);
  };

  // 添加待办事项
  const handleAddTodo = (text) => {
    const newTodo = {
      id: Date.now().toString(),
      text,
      completed: false,
    };
    saveTodos([...todos, newTodo]);
  };

  // 切换待办事项状态
  const handleToggleTodo = (id) => {
    const updatedTodos = todos.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    );
    saveTodos(updatedTodos);
  };

  // 编辑待办事项
  const handleEditTodo = (id, newText) => {
    const updatedTodos = todos.map(todo => 
      todo.id === id ? { ...todo, text: newText } : todo
    );
    saveTodos(updatedTodos);
  };

  // 删除待办事项
  const handleDeleteTodo = (id) => {
    Alert.alert(
      '确认删除',
      '确定要删除这个待办事项吗?',
      [
        {
          text: '取消',
          style: 'cancel',
        },
        {
          text: '删除',
          style: 'destructive',
          onPress: () => {
            const updatedTodos = todos.filter(todo => todo.id !== id);
            saveTodos(updatedTodos);
          },
        },
      ],
      { cancelable: true }
    );
  };

  // 渲染待办项
  const renderItem = ({ item }) => (
    <TodoItem
      todo={item}
      onPress={handleToggleTodo}
      onDelete={handleDeleteTodo}
      onEdit={handleEditTodo}
    />
  );

  return (
    <View style={globalStyles.container}>
      <Text style={globalStyles.title}>待办清单</Text>
      
      <TodoInput onAddTodo={handleAddTodo} />
      
      <FlatList
        data={todos}
        renderItem={renderItem}
        keyExtractor={item => item.id}
        style={globalStyles.list}
        ListEmptyComponent={
          <Text style={globalStyles.emptyText}>暂无待办事项</Text>
        }
      />
    </View>
  );
};

export default TodoListScreen;

4.2 添加删除动画

为了让删除操作更加直观,我们可以添加删除动画:

javascript
// components/TodoItem.js
import React, { useState, useRef } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, TextInput, Alert, Animated } from 'react-native';

const TodoItem = ({ todo, onPress, onDelete, onEdit }) => {
  const [isEditing, setIsEditing] = useState(false);
  const [editText, setEditText] = useState(todo.text);
  const [isDeleting, setIsDeleting] = useState(false);
  const slideAnim = useRef(new Animated.Value(0)).current;
  const opacityAnim = useRef(new Animated.Value(1)).current;

  const handleEdit = () => {
    const trimmedText = editText.trim();
    if (trimmedText) {
      onEdit(todo.id, trimmedText);
      setIsEditing(false);
    } else {
      Alert.alert('提示', '待办事项内容不能为空');
    }
  };

  const handleDelete = () => {
    Alert.alert(
      '确认删除',
      '确定要删除这个待办事项吗?',
      [
        {
          text: '取消',
          style: 'cancel',
        },
        {
          text: '删除',
          style: 'destructive',
          onPress: () => {
            setIsDeleting(true);
            // 添加删除动画
            Animated.parallel([
              Animated.timing(slideAnim, {
                toValue: 1,
                duration: 300,
                useNativeDriver: true,
              }),
              Animated.timing(opacityAnim, {
                toValue: 0,
                duration: 300,
                useNativeDriver: true,
              }),
            ]).start(() => {
              onDelete(todo.id);
            });
          },
        },
      ],
      { cancelable: true }
    );
  };

  return (
    <Animated.View 
      style={[
        styles.container,
        {
          transform: [{
            translateX: slideAnim.interpolate({
              inputRange: [0, 1],
              outputRange: [0, 300],
            }),
          }],
          opacity: opacityAnim,
        },
      ]}
    >
      <TouchableOpacity 
        style={styles.checkboxContainer} 
        onPress={() => onPress(todo.id)}
      >
        <View style={[styles.checkbox, todo.completed && styles.checkboxCompleted]}>
          {todo.completed && <Text style={styles.checkmark}>✓</Text>}
        </View>
      </TouchableOpacity>

      {isEditing ? (
        <TextInput
          style={[styles.input, todo.completed && styles.textCompleted]}
          value={editText}
          onChangeText={setEditText}
          onSubmitEditing={handleEdit}
          returnKeyType="done"
          autoFocus
        />
      ) : (
        <TouchableOpacity 
          style={styles.textContainer} 
          onPress={() => setIsEditing(true)}
        >
          <Text style={[styles.text, todo.completed && styles.textCompleted]}>
            {todo.text}
          </Text>
        </TouchableOpacity>
      )}

      <TouchableOpacity onPress={handleDelete} style={styles.deleteButton}>
        <Text style={styles.deleteText}>×</Text>
      </TouchableOpacity>
    </Animated.View>
  );
};

// 样式保持不变

export default TodoItem;

5. 增强读取功能

5.1 添加过滤功能

为了方便用户查看不同状态的待办事项,我们可以添加过滤功能:

javascript
// screens/TodoListScreen.js
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, StyleSheet, Alert, TouchableOpacity } from 'react-native';
import TodoItem from '../components/TodoItem';
import TodoInput from '../components/TodoInput';
import { storage } from '../utils/storage';
import { globalStyles } from '../styles/global';

const TodoListScreen = () => {
  const [todos, setTodos] = useState([]);
  const [filter, setFilter] = useState('all'); // 'all', 'active', 'completed'

  // 加载待办事项
  useEffect(() => {
    loadTodos();
  }, []);

  const loadTodos = async () => {
    const savedTodos = await storage.getTodos();
    setTodos(savedTodos);
  };

  const saveTodos = async (newTodos) => {
    setTodos(newTodos);
    await storage.saveTodos(newTodos);
  };

  // 添加待办事项
  const handleAddTodo = (text) => {
    const newTodo = {
      id: Date.now().toString(),
      text,
      completed: false,
    };
    saveTodos([...todos, newTodo]);
  };

  // 切换待办事项状态
  const handleToggleTodo = (id) => {
    const updatedTodos = todos.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    );
    saveTodos(updatedTodos);
  };

  // 编辑待办事项
  const handleEditTodo = (id, newText) => {
    const updatedTodos = todos.map(todo => 
      todo.id === id ? { ...todo, text: newText } : todo
    );
    saveTodos(updatedTodos);
  };

  // 删除待办事项
  const handleDeleteTodo = (id) => {
    Alert.alert(
      '确认删除',
      '确定要删除这个待办事项吗?',
      [
        {
          text: '取消',
          style: 'cancel',
        },
        {
          text: '删除',
          style: 'destructive',
          onPress: () => {
            const updatedTodos = todos.filter(todo => todo.id !== id);
            saveTodos(updatedTodos);
          },
        },
      ],
      { cancelable: true }
    );
  };

  // 过滤待办事项
  const filteredTodos = todos.filter(todo => {
    if (filter === 'active') return !todo.completed;
    if (filter === 'completed') return todo.completed;
    return true;
  });

  // 渲染待办项
  const renderItem = ({ item }) => (
    <TodoItem
      todo={item}
      onPress={handleToggleTodo}
      onDelete={handleDeleteTodo}
      onEdit={handleEditTodo}
    />
  );

  return (
    <View style={globalStyles.container}>
      <Text style={globalStyles.title}>待办清单</Text>
      
      <TodoInput onAddTodo={handleAddTodo} />
      
      {/* 过滤按钮 */}
      <View style={styles.filterContainer}>
        <TouchableOpacity 
          style={[styles.filterButton, filter === 'all' && styles.filterButtonActive]}
          onPress={() => setFilter('all')}
        >
          <Text style={[styles.filterText, filter === 'all' && styles.filterTextActive]}>全部</Text>
        </TouchableOpacity>
        <TouchableOpacity 
          style={[styles.filterButton, filter === 'active' && styles.filterButtonActive]}
          onPress={() => setFilter('active')}
        >
          <Text style={[styles.filterText, filter === 'active' && styles.filterTextActive]}>未完成</Text>
        </TouchableOpacity>
        <TouchableOpacity 
          style={[styles.filterButton, filter === 'completed' && styles.filterButtonActive]}
          onPress={() => setFilter('completed')}
        >
          <Text style={[styles.filterText, filter === 'completed' && styles.filterTextActive]}>已完成</Text>
        </TouchableOpacity>
      </View>
      
      <FlatList
        data={filteredTodos}
        renderItem={renderItem}
        keyExtractor={item => item.id}
        style={globalStyles.list}
        ListEmptyComponent={
          <Text style={globalStyles.emptyText}>
            {filter === 'all' ? '暂无待办事项' : 
             filter === 'active' ? '暂无未完成的待办事项' : '暂无已完成的待办事项'}
          </Text>
        }
      />
    </View>
  );
};

const styles = StyleSheet.create({
  filterContainer: {
    flexDirection: 'row',
    marginBottom: 15,
    justifyContent: 'space-between',
  },
  filterButton: {
    flex: 1,
    padding: 10,
    borderRadius: 5,
    backgroundColor: '#e0e0e0',
    marginHorizontal: 5,
    alignItems: 'center',
  },
  filterButtonActive: {
    backgroundColor: '#007AFF',
  },
  filterText: {
    color: '#333',
  },
  filterTextActive: {
    color: '#fff',
    fontWeight: 'bold',
  },
});

export default TodoListScreen;

5.2 添加统计功能

为了让用户了解待办事项的整体情况,我们可以添加统计功能:

javascript
// screens/TodoListScreen.js
import React, { useState, useEffect } from 'react';
import { View, Text, FlatList, StyleSheet, Alert, TouchableOpacity } from 'react-native';
import TodoItem from '../components/TodoItem';
import TodoInput from '../components/TodoInput';
import { storage } from '../utils/storage';
import { globalStyles } from '../styles/global';

const TodoListScreen = () => {
  const [todos, setTodos] = useState([]);
  const [filter, setFilter] = useState('all'); // 'all', 'active', 'completed'

  // 加载待办事项
  useEffect(() => {
    loadTodos();
  }, []);

  const loadTodos = async () => {
    const savedTodos = await storage.getTodos();
    setTodos(savedTodos);
  };

  const saveTodos = async (newTodos) => {
    setTodos(newTodos);
    await storage.saveTodos(newTodos);
  };

  // 添加待办事项
  const handleAddTodo = (text) => {
    const newTodo = {
      id: Date.now().toString(),
      text,
      completed: false,
    };
    saveTodos([...todos, newTodo]);
  };

  // 切换待办事项状态
  const handleToggleTodo = (id) => {
    const updatedTodos = todos.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    );
    saveTodos(updatedTodos);
  };

  // 编辑待办事项
  const handleEditTodo = (id, newText) => {
    const updatedTodos = todos.map(todo => 
      todo.id === id ? { ...todo, text: newText } : todo
    );
    saveTodos(updatedTodos);
  };

  // 删除待办事项
  const handleDeleteTodo = (id) => {
    Alert.alert(
      '确认删除',
      '确定要删除这个待办事项吗?',
      [
        {
          text: '取消',
          style: 'cancel',
        },
        {
          text: '删除',
          style: 'destructive',
          onPress: () => {
            const updatedTodos = todos.filter(todo => todo.id !== id);
            saveTodos(updatedTodos);
          },
        },
      ],
      { cancelable: true }
    );
  };

  // 清除已完成的待办事项
  const handleClearCompleted = () => {
    if (todos.some(todo => todo.completed)) {
      Alert.alert(
        '确认清除',
        '确定要清除所有已完成的待办事项吗?',
        [
          {
            text: '取消',
            style: 'cancel',
          },
          {
            text: '清除',
            style: 'destructive',
            onPress: () => {
              const updatedTodos = todos.filter(todo => !todo.completed);
              saveTodos(updatedTodos);
            },
          },
        ],
        { cancelable: true }
      );
    }
  };

  // 过滤待办事项
  const filteredTodos = todos.filter(todo => {
    if (filter === 'active') return !todo.completed;
    if (filter === 'completed') return todo.completed;
    return true;
  });

  // 统计信息
  const totalTodos = todos.length;
  const completedTodos = todos.filter(todo => todo.completed).length;
  const activeTodos = totalTodos - completedTodos;

  // 渲染待办项
  const renderItem = ({ item }) => (
    <TodoItem
      todo={item}
      onPress={handleToggleTodo}
      onDelete={handleDeleteTodo}
      onEdit={handleEditTodo}
    />
  );

  return (
    <View style={globalStyles.container}>
      <Text style={globalStyles.title}>待办清单</Text>
      
      <TodoInput onAddTodo={handleAddTodo} />
      
      {/* 统计信息 */}
      <View style={styles.statsContainer}>
        <Text style={styles.statsText}>
          共 {totalTodos} 项,{activeTodos} 项未完成,{completedTodos} 项已完成
        </Text>
        {completedTodos > 0 && (
          <TouchableOpacity onPress={handleClearCompleted}>
            <Text style={styles.clearText}>清除已完成</Text>
          </TouchableOpacity>
        )}
      </View>
      
      {/* 过滤按钮 */}
      <View style={styles.filterContainer}>
        <TouchableOpacity 
          style={[styles.filterButton, filter === 'all' && styles.filterButtonActive]}
          onPress={() => setFilter('all')}
        >
          <Text style={[styles.filterText, filter === 'all' && styles.filterTextActive]}>全部</Text>
        </TouchableOpacity>
        <TouchableOpacity 
          style={[styles.filterButton, filter === 'active' && styles.filterButtonActive]}
          onPress={() => setFilter('active')}
        >
          <Text style={[styles.filterText, filter === 'active' && styles.filterTextActive]}>未完成</Text>
        </TouchableOpacity>
        <TouchableOpacity 
          style={[styles.filterButton, filter === 'completed' && styles.filterButtonActive]}
          onPress={() => setFilter('completed')}
        >
          <Text style={[styles.filterText, filter === 'completed' && styles.filterTextActive]}>已完成</Text>
        </TouchableOpacity>
      </View>
      
      <FlatList
        data={filteredTodos}
        renderItem={renderItem}
        keyExtractor={item => item.id}
        style={globalStyles.list}
        ListEmptyComponent={
          <Text style={globalStyles.emptyText}>
            {filter === 'all' ? '暂无待办事项' : 
             filter === 'active' ? '暂无未完成的待办事项' : '暂无已完成的待办事项'}
          </Text>
        }
      />
    </View>
  );
};

const styles = StyleSheet.create({
  statsContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 15,
  },
  statsText: {
    fontSize: 14,
    color: '#666',
  },
  clearText: {
    fontSize: 14,
    color: '#007AFF',
  },
  filterContainer: {
    flexDirection: 'row',
    marginBottom: 15,
    justifyContent: 'space-between',
  },
  filterButton: {
    flex: 1,
    padding: 10,
    borderRadius: 5,
    backgroundColor: '#e0e0e0',
    marginHorizontal: 5,
    alignItems: 'center',
  },
  filterButtonActive: {
    backgroundColor: '#007AFF',
  },
  filterText: {
    color: '#333',
  },
  filterTextActive: {
    color: '#fff',
    fontWeight: 'bold',
  },
});

export default TodoListScreen;

6. 完整的增删改查功能

现在,我们已经实现了完整的增删改查功能,包括:

  1. 添加待办事项:支持添加新的待办事项,包含输入验证和动画效果
  2. 查看待办事项:支持查看所有待办事项,可按状态过滤
  3. 更新待办事项:支持切换待办事项的完成状态和编辑待办事项内容
  4. 删除待办事项:支持删除单个待办事项和清除所有已完成的待办事项,包含确认对话框和删除动画
  5. 统计功能:显示待办事项的总数、未完成数和已完成数

7. 测试功能

执行以下命令运行项目,测试增删改查功能:

bash
# 启动开发服务器
npx expo start

# 在 iOS 模拟器中运行
# 按 i

# 在 Android 模拟器中运行
# 按 a

# 在网页中运行
# 按 w

8. 总结

在本章节中,我们实现了待办清单应用的核心功能:增删改查。通过添加验证、动画效果、过滤功能和统计功能,我们提高了应用的用户体验。这些功能的实现不仅展示了 React Native 的基本用法,也为我们后续的开发打下了基础。

在接下来的章节中,我们将进一步完善本地数据存储和美化应用的用户界面。

© 2026 编程马·菜鸟教程 版权所有