For a to-do list app I want to make data persist using SQLite but I’m facing issues:
- Invariant Violation: "main" has not been registered. This can happen if:
* Metro (the local dev server) is run from the wrong folder. Check if Metro is running, stop it and restart it in the current project.
* A module failed to load due to an error and `AppRegistry.registerComponent` wasn't called., js engine: hermes
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:102:17 in reportException
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:148:19 in handleException
- ... 7 more stack frames from framework internals
- TypeError: Cannot convert null value to object, js engine: hermes
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:102:17 in reportException
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:148:19 in handleException
at node_modules/react-native/Libraries/Core/setUpErrorHandling.js:24:39 in handleError
at node_modules/expo/build/errors/ExpoErrorManager.js:25:19 in errorHandler
at node_modules/expo/build/errors/ExpoErrorManager.js:30:24 in <anonymous>
at node_modules/@react-native/polyfills/error-guard.js:49:36 in ErrorUtils.reportFatalError
at node_modules/metro-runtime/src/polyfills/require.js:225:40 in guardedLoadModule
at node_modules/metro-runtime/src/polyfills/require.js:127:23 in metroRequire
at http://192.168.1.61:19000/node_modules/expo/AppEntry.bundle?platform=android&dev=true&hot=false&strict=false&minify=false:null in global
TodoListScreen.js :
import React, { useState, useEffect } from "react";
import {
View,
StatusBar,
Text,
TextInput,
TouchableOpacity,
Image,
Appearance,
StyleSheet,
} from "react-native";
import Icon from "react-native-vector-icons/MaterialIcons";
import SQLite from "react-native-sqlite-storage";
const db = SQLite.openDatabase(
{
name: "TodoList",
location: "default",
},
() => {},
(error) => {
console.log(error);
}
);
const createTable = () => {
// create table if not exists
db.transaction((tx) => {
tx.executeSql(`CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT NOT NULL);`);
});
};
const TodoListScreen = () => {
const [todos, setTodos] = useState([]);
const [text, setText] = useState("");
const fetchTodos = () => {
db.transaction((tx) => {
tx.executeSql(
"SELECT * FROM todos",
[],
(_, { rows }) => {
setTodos(rows._array);
},
(_, error) => {
console.log("Error fetching todos: ", error);
}
);
});
};
const handleAddTodo = () => {
if (text.length > 0) {
db.transaction((tx) => {
tx.executeSql(
"INSERT INTO todos (text) VALUES (?)",
[todoText],
() => {
console.log("Todo added successfully");
fetchTodos();
setTodoText("");
},
(_, error) => {
console.log("Error adding todo: ", error);
}
);
});
}
};
const handleRemoveTodo = (id) => {
db.transaction((tx) => {
tx.executeSql(
"DELETE FROM todos WHERE id = ?",
[id],
() => {
console.log("Todo deleted successfully");
fetchTodos();
},
(_, error) => {
console.log("Error deleting todo: ", error);
}
);
});
};
const systemTheme = Appearance.getColorScheme(); // Retrieve the system theme
const [themeMode, setThemeMode] = useState(systemTheme);
useEffect(() => {
createTable(); // create table in db if not exists
fetchTodos(); // fetch db todos
const subscription = Appearance.addChangeListener(({ colorScheme }) => {
setThemeMode(colorScheme === "dark");
});
return () => {
subscription.remove();
};
}, []);
const toggleDarkMode = () => {
themeMode == "light" ? setThemeMode("dark") : setThemeMode("light");
};
const containerStyle =
themeMode == "light" ? styles.containerDark : styles.containerLight;
const iconColor = themeMode == "light" ? "white" : "#292929";
const textColor = themeMode == "light" ? "white" : "#292929";
return (
<View style={[styles.container, containerStyle]}>
<StatusBar style={styles.bar}></StatusBar>
<Text style={[styles.title, { color: textColor }]}>Da Fá</Text>
{todos.length > 0 && todos.map((todo) => (
<TouchableOpacity
key={todo.id}
style={styles.todoContainer}
onPress={() => handleRemoveTodo(todo.id)}
>
<Text style={[styles.delete, { borderColor: textColor }]}></Text>
<Text style={[styles.todoText, { color: textColor }]}>
{todo.text}
</Text>
</TouchableOpacity>
))}
<View style={styles.inputContainer}>
<TouchableOpacity style={styles.addButton} onPress={handleAddTodo}>
<Icon name="add" size={24} color={iconColor} />
</TouchableOpacity>
<TextInput
style={[styles.input, { color: textColor }]}
placeholder="Add a todo..."
placeholderTextColor={textColor}
value={text}
onChangeText={(value) => setText(value)}
onSubmitEditing={handleAddTodo}
/>
</View>
</View>
);
};
export default TodoListScreen;
I’m testing on Android Expo Go.
2
Answers
That error can sometimes be difficult to debug because the cause isn’t always apparent. The only thing that jumps out at me is that you’re using both Expo and the external
react-native-sqlite-storage
library. Expo can be a bit finicky with native dependencies, so that could be causing the issue.Can you try removing all of the SQLite logic from this screen, and just rendering an empty view? If that works, I would assume that
react-native-sqlite-storage
is the issue. You could try going through the library’s setup instructions again to make sure you did everything correctly. But you may need to switch to usingexpo-sqlite
(npx expo install expo-sqlite
). This library is provided by Expo, so should be guaranteed to work with your development environment.For the invariant Violation try this:
I discovered that my old app was installed in my android emulator. This fixed it for me:
It turns out the emulator retains each app it installs instead of starting out as a blank slate each time as I expected. When it starts up, it tries to load the app with the earliest install time. So, it kept trying to run my old app and contact the old Metro server instead of running my new app.