React Native Expo应用Android Beta版导航至含FlatList页面崩溃
问题:React Native Expo生产环境导航到含日期选择器页面崩溃
我是一名初级开发者,正在开发基于React Native、Expo和函数式组件的应用。导航到包含日期选择器的页面时,应用会崩溃并跳转到app.json设置的默认启动页。该页面日期选择器默认值为当前日期,选择日期后会查询expo-sqlite并将结果展示在FlatList中。本地用Expo Go测试一切正常,但发布到Android商店Beta版本时出现崩溃,且无法查看生产环境日志。
相关代码
import * as SQLite from "expo-sqlite"; import DateTimePicker from "@react-native-community/datetimepicker"; const Realizadas = ({ navigation }) => { const [date, setDate] = useState(new Date()); const [show, setShow] = useState(false); const [msj, setMsj] = useState(""); const [dataMarca, setDataMarca] = useState([]); const [placeHolder, setPlaceHolder] = useState("Select a date"); const db = openDatabase(); function openDatabase() { if (Platform.OS === "web") { return { transaction: () => { return { executeSql: () => {}, }; }, }; } const db = SQLite.openDatabase("myDB.db"); return db; } useEffect(() => { console.log("useEffect"); db.transaction((tx) => { tx.executeSql( "create table if not exists user(name text, lastname text, pass text);", [], (tx, result) => { console.log("Created!"); }, (tx, error) => { console.error("Error creating db", error); } ); tx.executeSql( "create table if not exists info (id integer primary key not null, date text, type text, state text, latitude text, longitude text);", [], (tx, result) => { }, (tx, error) => { console.error("Error: ", error); } ); }); fetchData(date); }, []); useFocusEffect( React.useCallback(() => { fetchData(date); }, []) ); const cambia = (event, fechaDatePicker) => { setShow(false); setDate(fechaDatePicker); fetchData(fechaDatePicker); }; const showDatepicker = () => { setShow(true); }; function fetchData(selectedDate) { var nuevaFecha; if (selectedDate == null || selectedDate == "") { var fechaNueva = new Date(); var fecha = fechaNueva.getDate(); //Current Date var month = fechaNueva.getMonth() + 1; //Current Month var year = fechaNueva.getFullYear(); //Current Year nuevaFecha = year + "-" + (month <= 9 ? "0" + month : month) + "-" + (fecha <= 9 ? "0" + fecha : fecha); } else { var fa = selectedDate; var anio = fa.getFullYear(); var mes = fa.getMonth() + 1; if (mes < 10) { mes = "0" + mes; } var dia = fa.getDate(); nuevaFecha = anio + "-" + mes + "-" + dia; } setPlaceHolder(nuevaFecha); //var query = "Select * from marcacion where estado='ENVIADO'"; var query = "Select * from marcacion where fecha like '%" + nuevaFecha + "%' and estado='ENVIADO';"; var params = []; db.transaction((tx) => { tx.executeSql( query, params, (tx, result) => { //console.log(result.rows._array); if (result.rows._array.length > 0) { setDataMarca(result.rows._array); console.log(dataMarca, result.rows._array); setMsj(""); } else { setDataMarca([]); console.log(dataMarca); setMsj("No se encontraron resultados"); } }, function (tx, err) { //console.log(err); Alert.alert("Error", "No se encuentran resultados"); setMsj("No se encontraron resultados"); setDataMarca([]); console.log(dataMarca); } ); }); } const renderItem = useCallback( ({ item }) => ( <CollapsibleView arrowStyling={{ size: 0 }} style={styles.itemPrincipal} title={ <Text style={{ color: "black", fontSize: 25 }}>{item.tipo}</Text> } > <Text style={styles.item}>Fecha marcación: {item.fecha}</Text> <Text style={styles.item}>Estado de envío: {item.estado}</Text> </CollapsibleView> ), [] ); return ( <View style={styles.viewInicio} {...panResponder.panHandlers}> <View style={styles.viewTop}> <View style={styles.viewRellenoIzquierda}></View> <View style={styles.viewTitulo}> <Text style={styles.textoTitulo}>REALIZADAS</Text> </View> <View style={styles.viewDrawer}> <TouchableOpacity style={styles.drawer} onPress={() => navigation.openDrawer()} > <FontAwesome5 name="bars" size={24} color="#161924" /> </TouchableOpacity> </View> </View> <View style={styles.viewDatePicker}> <TouchableOpacity onPress={showDatepicker}> <View style={styles.main}> <Text style={styles.vacio1}></Text> <View style={styles.vistaCalendario}> <Text style={styles.textoCalendario}>{placeHolder}</Text> </View> <View style={styles.calendario}> <Image source={require("../assets/calendario.png")} style={styles.logo} /> </View> </View> </TouchableOpacity> </View> {show && ( <DateTimePicker testID="dateTimePicker" format="YYYY-MM-DD" minDate="2020-01-01" maxDate="2050-12-31" mode={"date"} display="inline" value={date} onChange={cambia} style={styles.estiloDatePicker} /> )} <View style={styles.viewLista}> {dataMarca && ( <FlatList data={dataMarca} extraData={dataMarca} ListEmptyComponent={ <View style={styles.viewMensaje}> <View style={styles.vistaMsj}> <Text style={styles.textoMsj}>{msj}</Text> </View> </View> } renderItem={renderItem} keyExtractor={(item) => item.fecha} refreshing={true} /> )} </View> </View> ); }; export default Realizadas;
解决方案
- 修复数据库初始化时机:当前
const db = openDatabase();在组件顶层执行,会在组件挂载前初始化数据库,生产环境可能因异步或权限问题导致异常。将数据库初始化移到useEffect内部:
// 移除顶层的const db = openDatabase(); useEffect(() => { const db = openDatabase(); // 移到这里 // 后续的db.transaction逻辑... }, []);
- 修复useFocusEffect依赖问题:当前回调依赖为空数组,但内部使用了
date状态,会导致闭包捕获旧值,添加date到依赖数组:
useFocusEffect( React.useCallback(() => { fetchData(date); }, [date]) // 添加date依赖 );
- 处理DateTimePicker取消选择的边界情况:用户取消选择时
fechaDatePicker可能为null,添加判断避免无效查询:
const cambia = (event, fechaDatePicker) => { setShow(false); if (fechaDatePicker) { // 确保有选中日期再更新状态和查询 setDate(fechaDatePicker); fetchData(fechaDatePicker); } };
- 修正表名不一致并避免SQL注入:代码中创建的表是
info,但查询的是marcacion,这会引发生产环境查询错误;同时改用参数化查询避免注入风险:
// 创建表时修正表名和字段名 tx.executeSql( "create table if not exists marcacion (id integer primary key not null, fecha text, tipo text, estado text, latitude text, longitude text);", [], // ...回调 ); // fetchData里的查询改用参数化 var query = "Select * from marcacion where fecha like ? and estado='ENVIADO';"; var params = [`%${nuevaFecha}%`]; // 用参数替代字符串拼接
- 添加错误边界避免应用崩溃:给组件包裹错误边界,捕获渲染异常:
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, errorInfo) { // 可在此记录错误 } render() { if (this.state.hasError) { return <Text>页面加载出错,请重试</Text>; } return this.props.children; } } // 使用错误边界包裹组件 export default () => ( <ErrorBoundary> <Realizadas /> </ErrorBoundary> );
- 集成生产环境日志收集:用
expo-error-recovery捕获错误信息,方便后续排查:
import * as ErrorRecovery from 'expo-error-recovery'; // 在SQL查询错误回调中添加 function (tx, err) { ErrorRecovery.setRecoveryProps({ error: err.message, query: query }); // ...其他逻辑 }
内容的提问来源于stack exchange,提问作者Pepopig
相关产品推荐
相关产品推荐

