我正在开发一个 React Native 应用程序,使用 WatermelonDB 作为我的数据库框架。我遵循 Watermelons 文档制作模型、模型、数据库和助手;使用 withObserables 来显示数据,但它不断在标题中抛出错误。我仍然找不到任何解决方案来解决我的问题,我在这里向其他开发人员寻求帮助。这是我第一次使用 WatermelonDB。
应用程序.tsx
import * as React from 'react';
import MainContainer from './navigation/MainContainer';
import database, {initializeDatabase} from './model/database_users';
function App() {
React.useEffect(() => {
// Initialize the database when the component mounts
initializeDatabase()
.then(() => {
console.log('Database initialization complete');
// Continue with the rest of your application logic
})
.catch(error => {
console.error('Error initializing database:', error);
});
}, []);
return <MainContainer />;
}
export default App;
seedDatabase.js
import {User} from './model/model'; // Import your WatermelonDB model
async function seedDatabase() {
// Check if there are any users in the database
const existingUsersCount = await User.query().fetchCount();
// If the database is empty, insert the initial users
if (existingUsersCount === 0) {
try {
await User.withDatabase(async database => {
// Insert each user into the database
for (const userData of initialUsers) {
await database.action(async () => {
await User.create(user => {
user.name = userData.name;
user.email = userData.email;
user.password = userData.password;
});
});
}
});
console.log('Seed data inserted successfully');
} catch (error) {
console.error('Error inserting seed data:', error);
}
} else {
console.log('Database already seeded');
}
}
export default seedDatabase;
./model/model.js
import {Model} from '@nozbe/watermelondb';
import {field, text} from '@nozbe/watermelondb/decorators';
class User extends Model {
static table = 'users';
static associations = {
sites: {type: 'has_many', foreignKey: 'author'},
};
@text('name') name;
@text('email') email;
@text('password') password;
}
class Site extends Model {
static table = 'sites';
static associations = {
user: {type: 'belongs_to', key: 'author'},
};
@text('site_id') site_id;
@text('txt') txt;
@text('image') image;
@relation('user', 'author') author;
}
.model/shema.js
import {appSchema, tableSchema} from '@nozbe/watermelondb';
const shema = appSchema({
version: 4,
tables: [
tableSchema({
name: 'users',
columns: [
{name: 'name', type: 'string', isIndexed: true},
{name: 'password', type: 'string'},
{name: 'email', type: 'string'},
],
}),
tableSchema({
name: 'sites',
columns: [
{name: 'site_id', type: 'string', isIndexed: true},
{name: 'txt', type: 'string'},
{name: 'image', type: 'string'},
{name: 'author', type: 'string'},
],
}),
],
});
database_users.js
import {Database} from '@nozbe/watermelondb';
import SQLiteAdapter from '@nozbe/watermelondb/adapters/sqlite';
import {User} from './model';
import {shema} from './shema';
import migrations from './migrations';
import seedDatabase from '../seedDatabase';
const adapter = new SQLiteAdapter({
migrations,
shema,
jsi: true, // enable if Platform.OS === 'ios'
});
const database = new Database({
adapter,
modelClasses: [User],
});
// Initialize the database
export async function initializeDatabase() {
try {
await database.adapter.initialize();
console.log('Database initialized');
} catch (error) {
console.error('Error initializing database:', error);
throw error; // Propagate the error if initialization fails
}
}
export default database;
./navigation/components/UserListItem.js
import {Button, StyleSheet, Text, View} from 'react-native';
import {deleteUser} from '../../model/helpers';
import {withObservables} from '@nozbe/watermelondb/react';
function UserListItem({user}) {
return (
<View key={user.name} style={styles.userRow}>
<View style={styles.button}>
<Button title="DEL" onPress={() => deleteUser(user)} />
{/* For demo simplicity when we update a record we just update its minPlayers */}
</View>
<Text>{user.name} - </Text>
</View>
);
}
const enchance = withObservables(['user'], ({user}) => ({
user,
}));
UserListItem = enchance(UserListItem);
export default UserListItem;
const styles = StyleSheet.create({
userRow: {flexDirection: 'row', alignItems: 'center', marginVertical: 8},
button: {marginRight: 8, flexDirection: 'row'},
});
./navigation/screens/DetailsScreen.js
import * as React from "react";
import { View, Text } from "react-native";
export default function DetailsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<Text
onPress={() => navigation.navigate("Home")}
style={{ fontSize: 26, fontWeight: "bold" }}
>
Culturise
</Text>
</View>
);
}
./navigation/screens/SearchScreen.js
import * as React from 'react';
import {
StyleSheet,
KeyboardAvoidingView,
Platform,
Keyboard,
} from 'react-native';
import {Searchbar} from 'react-native-paper';
export default function SearchScreen({navigation}) {
const [searchQuery, setSearchQuery] = React.useState('');
var [isSearching, setIsSearching] = React.useState(false);
React.useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener(
'keyboardDidShow',
() => {
setIsSearching(true);
},
);
const keyboardDidHideListener = Keyboard.addListener(
'keyboardDidHide',
() => {
setIsSearching(false);
},
);
// makni listenere kada nema tipkovnice
return () => {
keyboardDidShowListener.remove();
keyboardDidHideListener.remove();
};
}, []);
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}>
<Searchbar
style={isSearching ? styles.bar1 : styles.bar}
placeholder="Search"
onChangeText={setSearchQuery}
value={searchQuery}
showLoading={true}
/>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
bar: {
height: 60,
position: 'relative',
width: '90%',
bottom: '-170%',
//flex: 1,
opacity: 1,
marginRight: 'auto',
marginLeft: 'auto',
lineHeight: 44,
borderTopLeftRadius: 40,
borderTopRightRadius: 40,
borderBottomRightRadius: 40,
borderBottomLeftRadius: 40,
backgroundColor: 'rgba(255, 255, 255, 1)',
color: 'rgba(136, 136, 136, 1)',
fontSize: 30,
textAlign: 'left',
fontFamily: 'Roboto',
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 1)',
borderStyle: 'solid',
},
container: {
flex: 1,
},
bar1: {
height: 60,
width: '90%',
bottom: '-3%',
opacity: 1,
marginRight: 'auto',
marginLeft: 'auto',
lineHeight: 44,
borderTopLeftRadius: 40,
borderTopRightRadius: 40,
borderBottomRightRadius: 40,
borderBottomLeftRadius: 40,
backgroundColor: 'rgba(255, 255, 255, 1)',
color: 'rgba(136, 136, 136, 1)',
fontSize: 30,
textAlign: 'left',
fontFamily: 'Roboto',
borderWidth: 1,
borderColor: 'rgba(255, 255, 255, 1)',
borderStyle: 'solid',
},
});
./navigation/screens/SettingsScreen.js
import {View, Text} from 'react-native';
import {User} from '../../model/model';
export default function SettingsScreen({navigation}) {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text
onPress={() => navigation.navigate('Home')}
style={{fontSize: 26, fontWeight: 'bold'}}>
{Settings}
</Text>
</View>
);
}
./navigation/screens/HomeScreen.js
import * as React from 'react';
import {View, Text} from 'react-native';
import {withObservables} from '@nozbe/watermelondb/react';
//import {User} from '../../model/model';
import {UserListItem} from '../components/UserListItem';
function HomeScreenComponent({navigation, users}) {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text
onPress={() => navigation.navigate('Home')}
style={{fontSize: 26, fontWeight: 'bold'}}>
{users.map(user => (
<UserListItem key={user.name} user={user} />
))}
</Text>
</View>
);
}
const enhance = withObservables([], ({database}) => ({
users: database.collections.get(User.table).query().observe(),
}));
const HomeScreen = enhance(HomeScreenComponent);
export default HomeScreen;
./navigation/MainContainer.js
import * as React from 'react';
import {Image} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';
import Feather from 'react-native-vector-icons/Feather';
// Screens
import HomeScreen from './screens/HomeScreen';
import DetailsScreen from './screens/DetailsScreen';
import SettingsScreen from './screens/SettingsScreen';
import SearchScreen from './screens/SearchScreen';
//Screen names
const homeName = 'Lucky';
const detailsName = 'Culturise';
const settingsName = 'Account';
const searchName = 'Search';
const Tab = createBottomTabNavigator();
function MainContainer() {
return (
<NavigationContainer>
<Tab.Navigator
initialRouteName={homeName}
screenOptions={({route}) => ({
tabBarIcon: ({color, size}) => {
let iconName;
let rn = route.name;
if (rn === homeName) {
iconName = 'lucky';
} else if (rn === detailsName) {
iconName = 'compass';
} else if (rn === settingsName) {
iconName = 'user';
} else if (rn === searchName) {
iconName = 'search';
}
if (iconName != 'lucky') {
return <Feather name={iconName} size={size} color={color} />;
} else {
return (
<Image
source={require('../assets/dice.png')}
style={{width: 24, height: 24}}
/>
);
}
},
})}
tabBarOptions={{
activeTintColor: 'black',
inactiveTintColor: 'grey',
labelStyle: {paddingBottom: 10, fontSize: 9},
style: {padding: 10, height: 70},
}}>
<Tab.Screen name={homeName} component={HomeScreen} />
<Tab.Screen name={detailsName} component={DetailsScreen} />
<Tab.Screen name={searchName} component={SearchScreen} />
<Tab.Screen name={settingsName} component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default MainContainer;
错误的完整日志:
TypeError: Cannot read property 'collections' of undefined
This error is located at:
in withObservables[] (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by BottomTabView)
in RCTView (created by View)
in View (created by Screen)
in RCTView (created by View)
in View (created by Background)
in Background (created by Screen)
in Screen (created by BottomTabView)
in RNSScreen
in Unknown (created by InnerScreen)
in Suspender (created by Freeze)
in Suspense (created by Freeze)
in Freeze (created by DelayedFreeze)
in DelayedFreeze (created by InnerScreen)
in InnerScreen (created by Screen)
in Screen (created by MaybeScreen)
in MaybeScreen (created by BottomTabView)
in RNSScreenContainer (created by ScreenContainer)
in ScreenContainer (created by MaybeScreenContainer)
in MaybeScreenContainer (created by BottomTabView)
in RNCSafeAreaProvider (created by SafeAreaProvider)
in SafeAreaProvider (created by SafeAreaProviderCompat)
in SafeAreaProviderCompat (created by BottomTabView)
in BottomTabView (created by BottomTabNavigator)
in PreventRemoveProvider (created by NavigationContent)
in NavigationContent
in Unknown (created by BottomTabNavigator)
in BottomTabNavigator (created by MainContainer)
in EnsureSingleNavigator
in BaseNavigationContainer
in ThemeProvider
in NavigationContainerInner (created by MainContainer)
in MainContainer (created by App)
in App
in RCTView (created by View)
in View (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer
in AwesomeProject1(RootComponent), js engine: hermes
ERROR TypeError: Cannot read property 'collections' of undefined
This error is located at:
in withObservables[] (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by BottomTabView)
in RCTView (created by View)
in View (created by Screen)
in RCTView (created by View)
in View (created by Background)
in Background (created by Screen)
in Screen (created by BottomTabView)
in RNSScreen
in Unknown (created by InnerScreen)
in Suspender (created by Freeze)
in Suspense (created by Freeze)
in Freeze (created by DelayedFreeze)
in DelayedFreeze (created by InnerScreen)
in InnerScreen (created by Screen)
in Screen (created by MaybeScreen)
in MaybeScreen (created by BottomTabView)
in RNSScreenContainer (created by ScreenContainer)
in ScreenContainer (created by MaybeScreenContainer)
in MaybeScreenContainer (created by BottomTabView)
in RNCSafeAreaProvider (created by SafeAreaProvider)
in SafeAreaProvider (created by SafeAreaProviderCompat)
in SafeAreaProviderCompat (created by BottomTabView)
in BottomTabView (created by BottomTabNavigator)
in PreventRemoveProvider (created by NavigationContent)
in NavigationContent
in Unknown (created by BottomTabNavigator)
in BottomTabNavigator (created by MainContainer)
in EnsureSingleNavigator
in BaseNavigationContainer
in ThemeProvider
in NavigationContainerInner (created by MainContainer)
in MainContainer (created by App)
in App
in RCTView (created by View)
in View (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer
in AwesomeProject1(RootComponent), js engine: hermes
最新尝试:尝试用seedDatabase函数填充数据库,没有解决问题
查看错误
TypeError: Cannot read property 'collections' of undefined
可以很好地提示问题所在。
来自
./navigation/screens/HomeScreen.js
const enhance = withObservables([], ({database}) => ({
users: database.collections.get(User.table).query().observe(),
}));
“数据库”参数未定义。
请注意,您没有在此处传递数据库
./navigation/MainContainer.js
<Tab.Screen name={homeName} component={HomeScreen} />
WatermelonDB 和 React Native 有几种方法来处理这个问题。我过去在使用 WatermelonDB 时所做的就是螺旋钻探。如果您这样做,在某些情况下您可能需要使用
this.props.database
,所以请注意这一点。
<Tab.Screen name={homeName} children={ () => <HomeScreen database={ database }/> }/>
您还可以使用提供商。 watermelonDB 文档 有一个关于其数据库提供程序的部分。虽然我建议阅读他们的文档以获取更多信息,但下面复制了一些代码。
import { DatabaseProvider } from '@nozbe/watermelondb/react'
render(
<DatabaseProvider database={database}>
{ // rest of the app that needs the database }
</DatabaseProvider>
)
有两种记录的使用数据库的方法
import { withDatabase, compose } from '@nozbe/watermelondb/react'
export default compose(
withDatabase,
withObservables([], ({ database }) => ({
blogs: database.get('blogs').query(),
}),
)(BlogList)
// Or
import { useDatabase } from '@nozbe/watermelondb/react'
const Component = () => {
const database = useDatabase()
}