使用Expo+SQLite数据库时事务的成功/错误回调均未触发
问题描述
我参考了Expo文档中关于SQLite的示例及其他相关示例,但代码始终无法正常运行。为调试问题,我添加了成功和错误回调,然而这两个回调均未被触发。以下是我的测试代码:
import React, { useState, useEffect } from 'react'; import { Platform, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View, Button, } from "react-native"; import * as SQLite from "expo-sqlite"; function App(props) { function openDatabase() { if (Platform.OS === "web") { return { transaction: () => { return { executeSql: () => {}, }; }, }; } const db = SQLite.openDatabase("db.db"); return db; }; const db = openDatabase(); const [text, setText] = useState(null); const [forceUpdate, forceUpdateId] = useForceUpdate(); useEffect(() => { console.log("Use Effect"); // this gets logged db.transaction((tx) => { tx.executeSql( "create table if not exists items (id integer primary key not null, done int, value text);", [], (_, res) => {console.log("Error initializing database!");}, (_, err) => {console.log("Database was initialized");} ); }, [], (_, res) => {console.log("Error initializing database!");}, (_, err) => {console.log("Database was initialized");}); }, []); return ( <View style={styles.container}> <Text>Test</Text> </View> ); } function useForceUpdate() { const [value, setValue] = useState(0); return [() => setValue(value + 1), value]; } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center' } }); export default App;
问题分析与修复
核心问题点
tx.executeSql回调顺序颠倒:executeSql的参数顺序为(SQL语句, 参数数组, 成功回调, 错误回调),你将成功提示与错误提示的回调写反,且顺序错误会导致逻辑完全混乱。db.transaction参数格式错误:transaction的正确参数结构是(事务处理函数, 事务错误回调, 事务成功回调),你在事务处理函数后额外添加了[],导致后续的回调函数无法被正确绑定,这是回调完全不触发的关键原因。- Web端模拟未处理回调:Web环境下的模拟数据库对象没有调用传入的回调函数,所以不会输出日志,需要补充回调触发逻辑。
useForceUpdate存在闭包问题:状态更新时直接使用value + 1会导致闭包陷阱,且未将其加入useEffect依赖数组(若后续需用它触发更新)。
修复后的代码
import React, { useState, useEffect } from 'react'; import { Platform, StyleSheet, Text, View } from "react-native"; import * as SQLite from "expo-sqlite"; function App(props) { function openDatabase() { if (Platform.OS === "web") { return { transaction: (txCallback) => { // Web端模拟事务逻辑,确保触发回调 const mockTx = { executeSql: (sql, params, successCb, errorCb) => { // 模拟SQL执行成功,调用成功回调 successCb?.(null, { rows: { _array: [] } }); } }; txCallback(mockTx); return {}; }, }; } const db = SQLite.openDatabase("db.db"); return db; }; const db = openDatabase(); const [text, setText] = useState(null); const [forceUpdate, forceUpdateId] = useForceUpdate(); useEffect(() => { console.log("Use Effect"); db.transaction( (tx) => { tx.executeSql( "create table if not exists items (id integer primary key not null, done int, value text);", [], // SQL执行成功回调 (_, res) => {console.log("Database was initialized");}, // SQL执行错误回调 (_, err) => {console.log("Error initializing database!", err);} ); }, // 事务整体错误回调 (err) => {console.log("Transaction failed!", err);}, // 事务整体成功回调 () => {console.log("Transaction completed successfully");} ); }, [forceUpdateId]); // 加入依赖,确保forceUpdate时重新执行 return ( <View style={styles.container}> <Text>Test</Text> </View> ); } function useForceUpdate() { const [value, setValue] = useState(0); // 使用函数式更新避免闭包问题 return [() => setValue(prev => prev + 1), value]; } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center' } }); export default App;
修复说明
- 调整
tx.executeSql的回调顺序,将成功提示与错误提示对应到正确的回调逻辑中。 - 移除
db.transaction参数中多余的[],让事务的错误和成功回调正常绑定并触发。 - 完善Web端模拟数据库的
executeSql方法,确保回调函数被触发,方便Web环境调试。 - 修复
useForceUpdate的状态更新逻辑,使用函数式更新避免闭包问题,并将forceUpdateId加入useEffect依赖数组(若后续需通过它触发组件更新)。
内容的提问来源于stack exchange,提问作者Appa
相关产品推荐
相关产品推荐

