Expo环境下React Native中SQLite使用异常的问题求助
问题描述
我正在通过Expo学习React Native,使用expo-sqlite库实现数据库初始化、数据插入和查询操作。当前遇到两个核心问题:
- 插入数据时无报错,但
await insert语句后的navigation.navigate无法执行,同时持续收到「Possible Unhandled Promise Rejection (id: 0)」警告 - 改用
then/catch处理Promise后警告消失,但仍无法跳转到AllPlaces页面
相关代码
SQLite操作代码
import * as SQLite from 'expo-sqlite'; import { Place } from '../models/place'; const database = SQLite.openDatabase('places.db'); export function init() { const promise = new Promise((resolve, reject) => { database.transaction((tx) => { tx.executeSql( `CREATE TABLE IF NOT EXISTS places ( id INTEGER PRIMARY KEY NOT NULL, title TEXT NOT NULL, imageUri TEXT NOT NULL, address TEXT NOT NULL, lat REAL NOT NULL, lng REAL NOT NULL )`, [], () => resolve(), (_, error) => reject(error) ) }) }) return promise; } export function insertPlace(place) { const promise = new Promise((resolve, reject) => { database.transaction((tx) => { tx.executeSql( `INSERT INTO places (title, imageUri, address, lat, lng) VALUES (?, ?, ?, ?, ?)`, [place.title, place.imageUri, place.address, place.lat, place.lng], (_, result) => resolve(result), (_, error) => reject(error) ) }) }); return promise; } export function fetchPlaces() { const promise = new Promise((resolve, reject) => { database.transaction((tx) => { tx.executeSql('SELECT * FROM places', [], (_, result) => { const places = [] for (const dp of result.rows._array) { places.push(new Place(dp.title, dp.imageUri, {address: dp.address, lat: dp.lat, lng: dp.lng}, dp.id)) } resolve(places) }, (_, error) => reject(error) ) }) }); return promise }
Place数据模型代码
export class Place { constructor(title, imageUri, location, id) { this.title = title; this.imageUri = imageUri; this.address = location.address; this.location = {lat: location.lat, lng: location.lng}; this.id = id } }
App.js代码
import { init } from './util/database'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { NavigationContainer } from '@react-navigation/native'; import { useState, useEffect } from 'react'; import { StatusBar } from 'react-native'; import AllPlaces from './screens/AllPlaces'; import AddPlace from './screens/AddPlace'; import Map from './screens/Map'; import { Colors } from './constants/colors'; import AppLoading from 'expo-app-loading'; const Stack = createNativeStackNavigator(); export default function App() { const [dbInitialized, setDBInitialized] = useState(false); useEffect(() => { init() .then(() => setDBInitialized(true)) .catch((err) => console.log(err)); }, []) if (!dbInitialized) { return <AppLoading /> } return ( <> <StatusBar style="auto" /> <NavigationContainer> <Stack.Navigator screenOptions={{ headerStyle: { backgroundColor: Colors.primary50 }, headerTintColor: Colors.gray700, contentStyle: { backgroundColor: Colors.gray700 } }}> <Stack.Screen name="AllPlaces" component={AllPlaces} options={({ navigation }) => ({ title: 'Your Favorite Places', headerRight: ({ tintColor }) => ( <IconButton icon="add" size={24} color={tintColor} onPress={() => navigation.navigate('AddPlace')} /> ) })} /> <Stack.Screen name="AddPlace" component={AddPlace} options={{ title: 'Add a new Place' }} /> <Stack.Screen name="Map" component={Map} /> </Stack.Navigator> </NavigationContainer> </> ); }
AddPlace.js初始代码
import PlaceForm from "../components/places/PlaceForm" import { insertPlace } from "../util/database"; export default function AddPlace({navigation}) { async function createPlaceHandler(place) { await insertPlace(place); navigation.navigate('AllPlaces', {place: place}); } return ( <PlaceForm onCreatePlace={createPlaceHandler} /> ) }
PlaceForm.js代码
import { useState, useCallback } from 'react'; import { ScrollView, View, Text, TextInput, Button, StyleSheet } from 'react-native'; import ImagePicker from './ImagePicker'; import LocationPicker from './LocationPicker'; import { Place } from '../../models/place'; export default function PlaceForm({onCreatePlace}) { const [enteredTitle, setEnteredTitle] = useState(); const [selectedImage, setSelectedImage] = useState(); const [pickedLocation, setPickedLocation] = useState(); function changeTitleHandler(enteredText) { setEnteredTitle(enteredText); } function takeImageHandler(imageUri) { setSelectedImage(imageUri); } const pickLocationHandler = useCallback((location) => { setPickedLocation(location); }, []); function savePlaceHandler() { const placeData = new Place(enteredTitle, selectedImage, pickedLocation); onCreatePlace(placeData); } return ( <ScrollView style={styles.form}> <View> <Text style={styles.label}>Title</Text> <TextInput style={styles.input} onChangeText={changeTitleHandler} value={enteredTitle} /> </View> <ImagePicker onTakeImage={takeImageHandler} /> <LocationPicker onPickLocation={pickLocationHandler} /> <Button onPress={savePlaceHandler}>Add Place</Button> </ScrollView> ) } const styles = StyleSheet.create({ form: { flex: 1, padding: 24 }, label: { fontWeight: 'bold', marginBottom: 4, color: 'white' }, input: { marginBottom: 16, padding: 8, backgroundColor: 'white', borderRadius: 4 } });
AllPlaces.js代码
import PlacesList from "../components/places/PlacesList"; import { useState, useEffect } from 'react'; import { useIsFocused } from '@react-navigation/native'; export default function AllPlace ({route}) { const [loadedPlaces, setLoadedPlaces] = useState([]); const isFocused = useIsFocused(); useEffect(() => { async function loadPlaces() { await fetchPlaces(); } if(isFocused) { loadPlaces(); } }, [isFocused]) return ( <PlacesList places={loadedPlaces} /> ) }
修改后的AddPlace.js代码
import PlaceForm from "../components/places/PlaceForm" import { insertPlace } from "../util/database"; export default function AddPlace({navigation}) { async function createPlaceHandler(place) { await insertPlace(place).then((result) => navigation.navigate('AllPlaces')).catch((error) => console.log(error)); } return ( <PlaceForm onCreatePlace={createPlaceHandler} /> ) }
问题分析与解决方案
核心问题1:SQL插入参数错误
insertPlace函数中,你直接读取place.lat和place.lng,但根据Place类的定义,经纬度数据存储在place.location对象下,这会导致插入时传入undefined,违反数据库表的NOT NULL约束,最终Promise被拒绝,后续代码无法执行。
修复代码(util/database.js):
export function insertPlace(place) { const promise = new Promise((resolve, reject) => { database.transaction((tx) => { tx.executeSql( `INSERT INTO places (title, imageUri, address, lat, lng) VALUES (?, ?, ?, ?, ?)`, // 修正经纬度的取值路径 [place.title, place.imageUri, place.address, place.location.lat, place.location.lng], (_, result) => resolve(result), (_, error) => reject(error) ) }) }); return promise; }
核心问题2:AllPlaces数据未更新
AllPlaces组件中,loadPlaces函数仅调用了fetchPlaces,但未将返回的数据赋值给状态,导致页面无法渲染新插入的数据。
修复代码(AllPlaces.js):
import { fetchPlaces } from "../util/database"; // 补充导入 import PlacesList from "../components/places/PlacesList"; import { useState, useEffect } from 'react'; import { useIsFocused } from '@react-navigation/native'; // 修正组件名拼写错误:AllPlace → AllPlaces export default function AllPlaces ({route}) { const [loadedPlaces, setLoadedPlaces] = useState([]); const isFocused = useIsFocused(); useEffect(() => { async function loadPlaces() { try { const places = await fetchPlaces(); setLoadedPlaces(places); // 将获取到的数据更新到状态 } catch (error) { console.error('加载数据失败:', error); } } if(isFocused) { loadPlaces(); } }, [isFocused]) return ( <PlacesList places={loadedPlaces} /> ) }
优化异步处理逻辑
AddPlace中混合使用await和then/catch的写法不规范,改用try/catch更清晰,便于排查错误:
优化代码(AddPlace.js):
import PlaceForm from "../components/places/PlaceForm" import { insertPlace } from "../util/database"; export default function AddPlace({navigation}) { async function createPlaceHandler(place) { try { await insertPlace(place); navigation.navigate('AllPlaces'); } catch (error) { console.error('插入数据失败:', error); } } return ( <PlaceForm onCreatePlace={createPlaceHandler} /> ) }
可选优化:添加表单校验
在PlaceForm的savePlaceHandler中添加必填项校验,避免插入无效数据:
function savePlaceHandler() { if (!enteredTitle || !selectedImage || !pickedLocation) { alert('请填写所有必填字段!'); return; } const placeData = new Place(enteredTitle, selectedImage, pickedLocation); onCreatePlace(placeData); }
内容的提问来源于stack exchange,提问作者Grace
相关产品推荐
相关产品推荐

