FlatList with Firestore? React Native - reactjs

I'm sure this is something that is pretty common but can't seem to get it figured out. Each user has a "books" array in Firestore, and this is what I want to be returned in the Flatlist, where am I going wrong? The first day with firebase so I'm pretty sure its something basic. Thanks :)
<FlatList
data={() => { db.collection('users').doc(userEmail).data() }}
numColumns={2}
ListEmptyComponent={
<View style={styles.flatListEmpty}>
<Text style={{ fontWeight: 'bold' }}>Add Books Below</Text>
</View>
}
renderItem={({ item }) => (
<View style={styles.flatListStyle}>
<Text>book</Text>
</View>
)}
/>
then this is the database:

The problem is that query to firebase is asynchronous action, so you need to resolve that action then store the result to the state of component and assign that state to flatlist. Example:
const Component = () => {
const [data, setData] = useState();
const getData = async () => {
const snapshot = await db.collection('users').doc(userEmail).get()
setData(snapshot.data())
}
useEffect(() => {
getData()
}, [])
return (
<FlatList
data={data.books}
numColumns={2}
ListEmptyComponent={
<View style={styles.flatListEmpty}>
<Text style={{ fontWeight: 'bold' }}>Add Books Below</Text>
</View>
}
renderItem={({ item }) => (
<View style={styles.flatListStyle}>
<Text>book</Text>
</View>
)}
/>
)
}

Related

Invalid hook call React Native FlatList Navigation

I'm making a notes app in React Native and trying to make it so I can click on a note in a FlatList to edit it. I'm using react-router-native for this. I get an Error when clicking on any FlatList item. I know that this error has been asked on stack overflow before but the answers are all for class components, whereas I'm using functional components.
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
import { FlatList, Pressable, StyleSheet, View } from "react-native"
import { useNavigate } from "react-router-native"
import theme from "../theme"
import Text from "./Text"
const styles = StyleSheet.create({
separator: {
height: 10,
backgroundColor: theme.colors.background,
},
item: {
padding: 8,
backgroundColor: "white",
},
})
const ItemSeparator = () => <View style={styles.separator} />
const renderItem = ({ item }) => (
<View style={styles.item}>
<Pressable onPress={() => useNavigate(`/${item.id}`)}>
<Text fontWeight="bold" fontSize="subheading">
{item.title}
</Text>
<Text>{item.body}</Text>
</Pressable>
</View>
)
const NoteList = ({ notes }) => {
return (
<FlatList
data={notes}
ItemSeparatorComponent={ItemSeparator}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
)
}
useNavigate is a React hook and can only be called by a React function component or other custom React hook. It cannot be called in nested functions/callbacks.
Move the useNavigate hook call to the NoteList component and refactor the renderItem callback to curry a passed navigate function.
const ItemSeparator = () => <View style={styles.separator} />;
const renderItem = (navigate) => ({ item }) => (
<View style={styles.item}>
<Pressable onPress={() => navigate(`/${item.id}`)}>
<Text fontWeight="bold" fontSize="subheading">
{item.title}
</Text>
<Text>{item.body}</Text>
</Pressable>
</View>
);
const NoteList = ({ notes }) => {
const navigate = useNavigate(); // <-- hook called in React function
return (
<FlatList
data={notes}
ItemSeparatorComponent={ItemSeparator}
renderItem={renderItem(navigate)} // <-- pass navigate
keyExtractor={(item) => item.id}
/>
);
};
Alternatively you could move the renderItem function declaration into the NoteList component so the navigate function is just closed over in callback scope.
const ItemSeparator = () => <View style={styles.separator} />;
const NoteList = ({ notes }) => {
const navigate = useNavigate();
const renderItem = ({ item }) => (
<View style={styles.item}>
<Pressable onPress={() => navigate(`/${item.id}`)}>
<Text fontWeight="bold" fontSize="subheading">
{item.title}
</Text>
<Text>{item.body}</Text>
</Pressable>
</View>
);
return (
<FlatList
data={notes}
ItemSeparatorComponent={ItemSeparator}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
);
};

#gorrhom React Native Bottom Sheet - Calling from another component

Im trying to implement this relatively popular bottom sheet in React Native by #gorhom.
My aim is to open the bottom sheet from another component. I've tried to follow this answer - here . However, i do not understand what goes in the touchable opacity onPress in my component where it is being called from.
Code below
BottomSheetModalComponent
export default function BottomSheetModalComponent({ref}) {
// ref
const bottomSheetModalRef = useRef<BottomSheetModal>(null);
// variables
const snapPoints = useMemo(() => ['25%', '50%'], []);
// callbacks
const handlePresentModalPress = useCallback(() => {
ref.current?.present();
}, []);
const handleSheetChanges = useCallback((index: number) => {
console.log('handleSheetChanges', index);
}, []);
// renders
return (
<BottomSheetModalProvider>
<View>
<BottomSheetModal
ref={ref}
snapPoints={snapPoints}
onChange={handleSheetChanges}
>
<View>
<Text>Awesome 🎉</Text>
</View>
</BottomSheetModal>
</View>
</BottomSheetModalProvider>
);
};
Location Component
export default function LocationBar() {
// Create Ref
const userBottomSheetRef = useRef<BottomSheetModal>(null);
// Pass ref into the bottom sheet component
<LocationBottomSheet ref={userBottomSheetRef} snapPoints={["30%"]}/>
return (
<>
<View style={{ flexDirection: "row" }}>
<View style={styles.locationBar}>
<Octicons style={styles.locationIcon} name="location" size={18} />
<TouchableOpacity onPress={() => {
//WHAT GOES HERE?
}}>
<Text style={{fontSize: 17, fontWeight: '600'}}>{userLocation}</Text>
</TouchableOpacity>
<Ionicons style={styles.chevronIcon} name="chevron-down" size={12} />
</View>
</View>
</>
);
}
Thanks in advance

Adding Realtime firebase data with useState to a flatlist

I want to take this data and display it in a flatlist. I tried the way described below, but it wont show me anything in the flatlist. How would i be able to solve this problem?
Code for fetching data
useEffect(() => {
refComment.once('value').then(snapshot=>{
var li=[]
snapshot.forEach((child) => {
li.push({
// avatarIcon : child.val().avatarIcon,
// commentText : child.val().commentText,
// name : child.val().name,
// time : child.val().time,
// id : child.key
key:child.key,
comment:child.val()
}
)
}
)
setcommentData(li)
console.log(li);
})
} , []);
code for my flatlist
<Card>
<View style={styles.answerDetailsView}>
<View style={styles.allContentView}>
<Text style={styles.allContentText}>Tüm yorumlar</Text>
</View>
<Divider/>
<FlatList
data={commentData}
keyExtractor={(item)=>item.key}
renderItem={({item,index})=>{
return(
<View
index={index}
style={styles.contentView}>
<View style={styles.commentatorDetailView}>
<View style={styles.commentatorAvatarView}>
<Avatar.Icon
size={30}
icon={item.avatarIcon}
/>
</View>
<View style={styles.commentatorNameAndTimeView}>
<Text>{item.name}</Text>
<Text>{item.time}</Text>
</View>
<View style={styles.commentatorLikeView}>
<TouchableOpacity>
<Icon
name='thumbs-up'
/>
</TouchableOpacity>
</View>
</View>
<View style={styles.contentTextView}>
<Text style={styles.contentText}>
{item.commentText}
</Text>
</View>
</View>
)
}}
/>
</View>
</Card>
And RealTime firebase screen
enter image description here
my mobile screenshot
enter image description here
Try to refactor logic to fetch comments as below:
function getComments() {
const commentsRef = firebase.database().ref("comments");
commentsRef.on("value", (snapshot) => {
if (snapshot.val()) {
const data = snapshot.val();
const comments = Object.values(data) || [];
setcommentData(comments);
}
});
}
useEffect(() => {
getComments();
}, []);

Invariant Violation: Text strings must be rendered within a <Text> component while using flatList

I am using flat list to display data which is coming from unsplash api. But here it keeps on complaining to saying this
Invariant Violation: Text strings must be rendered within a component
I am not even using any text component. I have no idea what is wrong here.
App.js
export default function App() {
const [loading, setLoading] = useState(true);
const [image, setImage] = useState([]);
const {height, width} = Dimensions.get('window');
const URL = `https://api.unsplash.com/photos/random?count=30&client_id=${ACCESS_KEY}`;
useEffect(() => {
loadWallpapers();
}, [])
const loadWallpapers =() => {
axios.get(URL)
.then((res) => {
setImage(res.data);
setLoading(false);
}).catch((err) => {
console.log(err)
}).finally(() => {
console.log('request completed')
})
}
const renderItem = (image) => {
console.log('renderItem', image);
return (
<View style={{height, width}}>
<Image
style={{flex: 1, height: null, width: null}}
source={{uri : image.urls.regular}}/>
</View>
)
}
return loading ? (
<View style={{flex: 1, backgroundColor: 'black', justifyContent: 'center',alignItems: 'center'}}>
<ActivityIndicator size={'large'} color="grey"/>
</View>
): (
<SafeAreaView style={{flex: 1, backgroundColor: 'black'}}>
<FlatList
horizontal
pagingEnabled
data={image}
renderItem={({ item }) => renderItem(item)} />}
/>
</SafeAreaView>
)
}
I thing data of Flatlist is null, try
<FlatList
horizontal
pagingEnabled
data = {image ? image : []}
renderItem={({ item }) => renderItem(item)} />}
/>
I needed to do something like this to make it work.
const renderItem = ({ item }) => { <---- I have destructured item here
console.log(item)
return (
<View style={{ flex: 1 }}>
</View>
);
};
<FlatList
scrollEnabled={!focused}
horizontal
pagingEnabled
data={image}
renderItem={renderItem}
/>

How to use map function to Mobx observable state?

Now I would like to use Map function to show a component with each array.
But, my code with map is not working rightly.
In firestore, there are 3 document in "food" collection but this map function can only render one document.
This is my code below in render component.
renderItemBox() {
const { store } = this.props;
const items = store.restaurantStore.Items;
return items.map((value, index) => {
console.log(items);
return (
<TouchableOpacity key={index}>
<View style={styles.itemsBox}>
<View style={styles.itemsImageBox}>
<Image
source={require('../../assets/Images/Home/HomeListImageShop.jpg')}
style={styles.itemsImage}
/>
</View>
<View style={{ flexDirection: 'row' }}>
<Text style={styles.itemsName}>
{value[index].name}
</Text>
<Text style={styles.itemsTag}>
{value[index].tag}
</Text>
</View>
<View style={{ flexDirection: 'row' }}>
<Text style={styles.itemsDescription}>
{value[index].shortDescription}
</Text>
<TouchableOpacity
style={styles.likeButtonBox}
onPress={this.handleLikeButton.bind(this)}
>
{this.handleLikeImage()}
</TouchableOpacity>
</View>
</View>
</TouchableOpacity>
);
});
}
In addition to this, I set store component in my project.
This code above is importing observable state(array) from that store.
Below one is that store.
import { observable, action } from 'mobx';
import firebase from 'firebase';
class RestaurantStore {
#observable Items = [];
#action handleFirestoreCollectionOfFoods () {
const db = firebase.firestore();
db.collection('foods')
.get()
.then((snapshot) => {
const preItems = [];
snapshot.forEach((doc) => {
const docData = doc.data();
preItems.push(docData);
});
this.Items.push(preItems);
})
.catch((error) => {
console.log(error);
});
}
}
export default RestaurantStore;
Fortunetely, this store can import complete document from firestore.
I did check that with console.log.
So, does someone know how to use map function to mobx state completely?

Resources