You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native中expo-sqlite存储任务重启后消失的问题求助

React Native任务数据持久化问题排查与修复

我正在开发一款管理笔记和任务的React Native应用,分别为两者创建了对应的数据表。笔记的保存和展示功能完全正常,但任务功能存在问题:任务创建后能在组件中正常显示,但重新加载或关闭应用后就会消失。任务相关组件逻辑和笔记组件基本一致,仅针对任务做了少量扩展。以下是任务创建、展示及容器组件的代码,求帮助排查问题。

创建任务组件

import React, { useState } from 'react';
import { ScrollView, StyleSheet, Text, View, TextInput, Pressable } from 'react-native';
import Icon from "react-native-vector-icons/Ionicons";
import { useDataContext } from '../dataProviders/DataProvider';
import { db } from '../database/database';

export default function CreateTasks() {
    const { handleAddTask } = useDataContext();

    const [taskTitle, setTaskTitle] = useState('');
    const [taskDate, setTaskDate] = useState('');
    const [taskTime, setTaskTime] = useState('');
    const [taskDescription, setTaskDescription] = useState('');

    const addTask = () => {
        const newValue = {
            taskTitle: taskTitle,
            date: taskDate,
            time: taskTime,
            taskDescription: taskDescription,
        };
        setTaskTitle(newValue.taskTitle);
        setTaskDate(newValue.date);
        setTaskTime(newValue.time);
        setTaskDescription(newValue.taskDescription);
        db.transaction(
            (tx) => {
                tx.executeSql(
                    'insert into tasks ( title, date, time, description) values(?, ?, ?, ?)',
                    [newValue.taskTitle, newValue.date, newValue.time, newValue.taskDescription]
                );
            },
        );
        handleAddTask(newValue);
        setTaskTitle(''); // Clear the input fields
        setTaskDate('');
        setTaskTime('');
        setTaskDescription('');
    };

    return (
        <View style={styles.container}>
           <ScrollView style={styles.inputArea}>
                <TextInput
                    style={styles.inputTitle}
                    value={taskTitle}
                    onChangeText={taskTitle => setTaskTitle(taskTitle)}
                    placeholder='Task'
                />
                <TextInput
                    style={styles.inputDate}
                    value={taskDate}
                    onChangeText={date => setTaskDate(date)}
                    keyboardType='default'
                    placeholder='xx/xx/xxxx'
                />
                <TextInput
                    style={styles.inputTime}
                    value={taskTime}
                    onChangeText={time => setTaskTime(time)}
                    keyboardType='default'
                    placeholder='00:00'
                />
                <TextInput
                    style={styles.input}
                    value={taskDescription}
                    onChangeText={taskDescription => setTaskDescription(taskDescription)}
                    multiline={true}
                    keyboardType='default'
                    placeholder='Description...'
                />
            </ScrollView>
            <View style={styles.buttonContainer}>
                <Pressable onPress={() => addTask()}>
                    <Icon
                        name={"bookmark"}
                        //color="#87CEFA"
                        size={40}
                    />
                </Pressable>
            </View> 
        </View>  
    )
}

展示任务组件

import React, { useState, useEffect } from 'react';
import { ScrollView, StyleSheet, Text, View, TextInput, Pressable, Alert } from 'react-native';
import TaskContainer from './TaskContainer';
import EditTasks from './EditTasks';
import { useDataContext } from '../dataProviders/DataProvider';
import { getTasks, insertIntoDeletedTasks, removeTask } from '../database/database';
import { Provider } from 'react-native-paper';
import Icon from "react-native-vector-icons/Ionicons";

export default function Tasks() {
  const { taskItems, setTaskItems } = useDataContext();

  useEffect(() => {
    (async () => {
      try {
        const fetchedTasks = await getTasks();
        setTaskItems(fetchedTasks);
      } catch (error) {
        console.error('Error fetching notes:', error);
        Alert.alert('Error fetching tasks. Please try again.');
      }
    })();
  }, []);

  const [searchQuery, setSearchQuery] = useState('');
    
  const regex = new RegExp(searchQuery, 'i');
  const filteredData = taskItems.filter(item =>
      regex.test(item.taskTitle)
  );

  const moveTaskToDeletedTasks = async (taskData) => {
    try {
      insertIntoDeletedTasks(taskData)
    } catch (error) {
      console.error('Error moving note to deletedNotes:', error);
      throw new Error('Error moving note to deletedNotes.');
    }
  };

  const deleteTask = (index) => {
    // Show a confirmation dialog
    Alert.alert(
      'Delete Task',
      'Are you sure you want to delete this task?',
      [
        {
          text: 'Cancel',
          style: 'cancel',
        },
        {
          text: 'Delete',
          style: 'destructive',
          onPress: () => {
            // If the user confirms, delete the task
            let deletedTask = taskItems[index];
            // Move the note to deletedNotes table
            moveTaskToDeletedTasks(deletedTask);

            // Remove the note from noteItems
            let itemsCopyTask = [...taskItems];
            itemsCopyTask.splice(index, 1);
            setTaskItems(itemsCopyTask);

            // Perform the actual deletion of the note from the Notes table
            removeTask(deletedTask);
          },
        },
      ],
      { cancelable: true }
    );
  }

  return (
    <Provider>
      <View style={styles.mainContainer}>
        <View style={styles.searchBox}>
          <TextInput
            placeholder="Search..."
            onChangeText={(text) => setSearchQuery(text)}
            value={searchQuery}
            style={styles.input}
          />
          <Icon
            name="search-outline"
            size={40}
          />
        </View>
        <ScrollView>
          <View style={styles.container}>
            {filteredData.map((item, index) => (
              item && item.taskTitle ? ( // Check if 'item' exists and has a 'title' property
                <Pressable
                  key={index}
                  onPress={() => deleteTask(index)}
                >
                  <TaskContainer
                    taskTitle={item.taskTitle}
                    date={item.date}
                    time={item.time}
                    taskDescription={item.taskDescription}
                    index={index}
                    task={item}
                  />
                </Pressable>
              ) : null
            ))}
          </View>
        </ScrollView>
        <EditTasks />
      </View>
    </Provider>
  )
}

任务容器组件

import React from 'react';
import { StyleSheet, Text, View, Pressable } from 'react-native';
import Icon from "react-native-vector-icons/Ionicons";
import { useDataContext } from '../dataProviders/DataProvider';


export default function TaskContainer(props) {

    const { showDialogTask } = useDataContext();
    const { task } = props;

    return (
        <View styles={styles.container}>
            <View style={styles.TaskContainer}>
                <View style={styles.box1}>
                    <View style={styles.box11}>
                        <Text style={styles.titleItem}>Task</Text>
                        <Text style={styles.text} ellipsizeMode="tail" numberOfLines={1}>{task.taskTitle}</Text>
                    </View>
                </View>
                <View style={styles.box2}>
                    <View style={styles.box22}>
                        <Text style={styles.titleItem}>Date</Text>
                        <Text style={styles.text}>{task.date}</Text>
                    </View>
                    <View style={styles.box22}>
                        <Text style={styles.titleItem}>Time</Text>
                        <Text style={styles.text}>{task.time}</Text>
                    </View>
                </View>
                <View style={styles.box3}>
                    <View>
                        <Text style={styles.text} ellipsizeMode="tail" numberOfLines={1}>{task.taskDescription}</Text>
                    </View>
                </View>
                <View style={styles.box4}>
                    <Pressable onPress={() => showDialogTask(task)}>
                        <Icon
                            name="create-outline"
                            size={40}
                        />
                    </Pressable>
                </View>
            </View>
        </View>
    );
}

问题排查与修复方案

1. 数据库插入逻辑异步处理缺失

CreateTasks组件的addTask函数中,SQLite事务是异步操作,但当前代码未等待事务完成就更新上下文状态并清空输入,可能导致数据未写入数据库就刷新状态,或事务失败无错误提示。

修复:
给事务添加成功/失败回调,确保数据写入后再更新状态:

const addTask = () => {
    const newValue = {
        taskTitle: taskTitle,
        date: taskDate,
        time: taskTime,
        taskDescription: taskDescription,
    };

    db.transaction(
        (tx) => {
            tx.executeSql(
                'insert into tasks ( title, date, time, description) values(?, ?, ?, ?)',
                [newValue.taskTitle, newValue.date, newValue.time, newValue.taskDescription],
                (_, result) => {
                    // 插入成功后更新上下文,带上数据库自增ID
                    handleAddTask({...newValue, id: result.insertId});
                    // 清空输入
                    setTaskTitle('');
                    setTaskDate('');
                    setTaskTime('');
                    setTaskDescription('');
                }
            );
        },
        (error) => {
            console.error('任务插入失败:', error);
            alert('保存任务失败,请重试');
        }
    );
};

2. 数据库字段与组件字段映射不匹配

数据库表中字段是title和description,但组件中使用的是taskTitle和taskDescription,getTasks查询返回的数据结构未做映射,导致读取后字段不匹配,无法正确渲染或存储。

修复:
在database.js的getTasks函数中映射字段:

export const getTasks = async () => {
    return new Promise((resolve, reject) => {
        db.transaction((tx) => {
            tx.executeSql('select * from tasks', [], (_, { rows }) => {
                const tasks = rows._array.map(item => ({
                    id: item.id,
                    taskTitle: item.title,
                    date: item.date,
                    time: item.time,
                    taskDescription: item.description
                }));
                resolve(tasks);
            }, (_, error) => {
                reject(error);
            });
        });
    });
};

3. 任务容器组件样式语法错误

TaskContainer最外层View使用了styles={styles.container},正确语法应为style={styles.container}(多了一个s),虽不影响数据持久化,但建议修复以保证样式生效。

4. 上下文状态初始化验证

检查DataProvider中taskItems初始值是否为数组,handleAddTask是否正确将新任务添加到数组中,确保状态更新逻辑和笔记组件一致。


内容的提问来源于stack exchange,提问作者Alexandre Leal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 17:57:06