I am trying to randomize and only show 1 data from firebase and having some difficulties with it. I tried using random query inside the data array but it didn't work. Any ideas on how I can randomizerewards?
const Fetch = () => {
const [rewards, setRewards] = useState([]);
const todoRef = firebase.firestore().collection('Reward');
// const random = Math.floor(Math.random() * rewards)+1;
// todoRef = setRewards.whereField("random" <= random)
useEffect(() => {
todoRef
.limit(1) // limits the data to 1
// .orderBy('createdAt','desc') //random?
.onSnapshot(
querySnapshot => {
const rewards = []
querySnapshot.forEach((doc) => {
const {heading} = doc.data()
rewards.push({ id:doc.id, heading})
})
setRewards(rewards)
}
)
}, [])
heading is the data from firebase.
return (
<View style={{ flex:1, marginTop:100}}>
<Image source= {require('../assets/Congratulation.png')} style = {{ width: 400, height: 180 }}/>
<Text style={styles.text}> Contact your superiors to claim this reward.</Text>
<FlatList
style={{height:'100%'}}
data={rewards}
numColumns={1}
numRows={1}
renderItem={({item}) => (
<Pressable style={styles.container}>
<View style={styles.innerContainer}>
<Text style={styles.itemHeading}> {item.heading}</Text>
</View>
</Pressable>
)}
/>
</View>
Related
I am attempting to press on this pressable button, and navigate to a new page. The tricky bit is that this Pressable item is part of a returned array, as there are multiple of them being rendered each with different data. I want each button to take me to a 'product page', each page being different depending on the button
Here is what i have so far:
The main function
const JobRequestList = () => {
const [data, setData] = useState([]);
useEffect(() => {
returnArray().then(data2 => {
setData(data2);
});
}, []);
if (data.length === 0) {
j = [];
return (
<ScrollView>
<View key={'ERROR'} style={styles.wrapperERROR}>
<Text style={styles.textError}> {'No Current Job Requests'} </Text>
</View>
</ScrollView>
);
} else {
return <ScrollView>{data}</ScrollView>;
}
};
This requests the data, and returns it in a form that can be rendered. It either returns a no object, or an array of items from the below function - This is where my onPress is located, and have no idea how to implement a navigation fnction into it. Please note, i already have my navigation functions setup
const returnArray = async () => {
return queryData().then(() => {
return j.map(x => {
return (
<Pressable
key={x.id}
style={styles['wrapper' + x.data().PD]}
onPress={() => {}}>
<Text style={styles.text}> {x.data().PD} </Text>
<Text style={styles.text}> {x.data().address} </Text>
<Text style={styles.text}> {x.data().time} </Text>
</Pressable>
);
});
});
};
The above function then calls the below
const queryData = async () => {
await firestore()
.collection('Jobs')
.where('driver', '==', 'TBA') //TODO ADD CUSTOMER DISTANCE
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
j.push(doc);
});
});
};
Here is what my navigation functions should be inside this class - Again, which is already setup correctly
const navigation = useNavigation();
navigation.navigate('JobInfo');
Thankyou in advanced!
It is anti-pattern in React to store JSX in component state. React components's rendered UI is a function of state & props. Store the data in state and then render the data mapped to JSX.
Example:
queryData fetches firebase docs & data
const queryData = async () => {
await firestore()
.collection('Jobs')
.where('driver', '==', 'TBA') //TODO ADD CUSTOMER DISTANCE
.get()
.then(querySnapshot => {
const docs = [];
querySnapshot.forEach(doc => {
docs.push({
...doc,
data: doc.data(),
});
});
return docs;
});
};
Apply the navigation logic in the Pressable component's onPress handler when mapping the data state.
const JobRequestList = () => {
const navigation = useNavigation();
const [data, setData] = useState([]);
useEffect(() => {
queryData()
.then(data => {
setData(data);
});
}, []);
return (
<ScrollView>
{data.length
? data.map(el => (
<Pressable
key={el.id}
style={styles['wrapper' + el.data.PD]}
onPress={() => {
navigation.navigate('JobInfo');
}}
>
<Text style={styles.text}> {el.data.PD} </Text>
<Text style={styles.text}> {el.data.address} </Text>
<Text style={styles.text}> {el.data.time} </Text>
</Pressable>
))
: (
<View key={'ERROR'} style={styles.wrapperERROR}>
<Text style={styles.textError}> {'No Current Job Requests'} </Text>
</View>
)
}
</ScrollView>
);
};
I'm trying to fetch documents from a collection in Firestore and show it through a FlatList. But it shows a loading circle (IDK what it is actually called)!
I am using my own phone to test the app if it makes any difference. (I am quite new to this)
Here is a screenshot of my items screen where items are to be displayed:
Here is my code:
const Items = () => {
const [isLoading, setIsLoading] = useState(false)
const [isMoreLoading, setIsMoreLoading] = useState(false)
const [last, setLast] = useState(null)
const [items, setItems] = useState([])
let onEndReachedCalledDuringMomentum = false;
const itemsLoc = db.collection('items')
useEffect(() => {
getItems();
}, []);
getItems = async () => {
setIsLoading(true);
const snapshot = await itemsLoc.get();
if(!snapshot.empty){
let newItems = [];
setLast(snapshot.docs[snapshot.docs.length-1]);
for (let i = 0; i < snapshot.docs.length; i++){
newItems.push(snapshot.docs[i].data());
}
setItems(newItems)
}else{
setLast(null);
}
setIsLoading(false);
}
getMore = async () => {
if (last){
setIsMoreLoading(true);
let snapshot = await itemsLoc.orderBy('id').startAfter(last.data().id).limit(3).get();
if(!snapshot.empty){
let newItems = items;
setLast(snapshot.docs[snapshot.docs.length - 1]);
for (let i = 0 ; i < snapshot.docs.length; i++){
newItems.push(snapshot.docs[i].data());
}
setItems(newItems);
if(snapshot.docs.length < 3) setLast(null);
}else{
setLast(null)
}
setIsMoreLoading(false);
}
onEndReachedCalledDuringMomentum = true;
}
renderList = ({name,desc,image}) => {
return(
<View style={styles.container}>
<Image source={{uri: image}} style={styles.imageContainer}/>
<View style={styles.itemInfoContainer}>
<View>
<Text style={styles.title}>{name}</Text>
</View>
<View>
<Text style={styles.description}>{desc}</Text>
</View>
</View>
</View>
)
}
renderFooter = () => {
if (isMoreLoading) {return true;}
return <ActivityIndicator size="large" color="#64aeae" style={{ marginBottom:10 }}/>
}
onRefresh = () => {
getItems();
}
return(
<View style={{marginTop: 20}}>
<FlatList
showsVerticalScrollIndicator={false}
data={items}
keyExtractor={item => item.id}
renderItem={({item}) => renderList(item)}
ListFooterComponent = {renderFooter}
initialNumToRender={3}
onEndReachedThreshold = {0.1}
refreshing={
<RefreshControl
refreshing = {isLoading}
onRefresh = {onRefresh}
/>
}
onMomentumScrollBegin={() => onEndReachedCalledDuringMomentum = false}
onEndReached = {() => {
if (!onEndReachedCalledDuringMomentum && !isMoreLoading){
getMore();
}
}}
/>
</View>
I referenced the images incorrectly in the firebase. I used the path of the local storage where the images are stored rather than using the given URL of the image.
I'm working on a project that allows users to write anonymous letters addressed to people by name and I want to add a like/dislike functionality to the app. I was confused on how to get the specific document ID for that post and also incrementing the likeCount by 1 (in the areas where the "????" are at in the code below) ? I want it to update the field "likeCount" in firebase by 1 when the thumbs up icon is pressed.
This is the portion of my code that contains the posts (data from firebase) that is mapped for each firebase document:
function Home() {
const [posts, setPosts] = useState([]);
const [searchValue, setSearchValue] = useState("");
const [filteredPosts, setFilteredPosts] = useState([]);
const collectionRef = collection(db, "posts");
useEffect(() => {
const getPosts = async () => {
const data = await getDocs(collectionRef);
const filteredRef = query(
collectionRef,
where(`recipiant`, "==", `${searchValue}`)
);
const querySnapshot = await getDocs(filteredRef);
let posts = [];
querySnapshot.forEach((doc) => {
posts.push(doc.data());
});
setFilteredPosts(posts);
setPosts(
searchValue
? filteredPosts
: data.docs.map((doc) => ({ ...doc.data() }))
);
};
getPosts();
}, [searchValue, filteredPosts]);
return (
<ImageBackground source={image} style={styles.image}>
<SafeAreaView style={styles.container}>
<ScrollView>
<View style={styles.header}>
<Text style={styles.title}>Home</Text>
</View>
<Pressable>
<Input
placeholder="Search for a name"
inputContainerStyle={styles.searchbar}
inputStyle={styles.searchInput}
placeholderTextColor="gray"
onChangeText={(text) => setSearchValue(text)}
/>
</Pressable>
{posts.map((post, key) => {
return (
<View style={styles.postWrapper} key={key}>
<View style={styles.btnWrapper}>
<View style={styles.likeBtn}>
<Icon
name="thumbs-up"
size={25}
color="#fff"
onPress={() => {
const postRef = doc(db, "posts", `????`);
updateDoc(postRef, {
likeCount: ????,
});
}}
/>
<Text style={styles.likeCount}>{post.likeCount}</Text>
</View>
<Icon
name="thumbs-down"
size={25}
color="#fff"
onPress={() => {}}
/>
</View>
<Card
containerStyle={{
backgroundColor: "rgba( 255, 255, 255, 0.5 )",
borderRadius: 50,
height: 300,
marginBottom: 25,
width: 330,
backdropFilter: "blur( 20px )",
padding: 20,
}}
>
<Card.Title style={styles.notepadHeader}>Message.</Card.Title>
<View style={styles.center}>
<ScrollView>
<Text style={styles.notepadText}>
To: <Text style={styles.name}>{post.recipiant}</Text>
</Text>
<Text style={styles.notepadTextLetter}>
{post.letter}
</Text>
<Text style={styles.notepadFooter}>
From:{" "}
<Text
style={{
color: "#9e4aba",
fontSize: 20,
}}
>
{post.displayName}
</Text>
</Text>
</ScrollView>
</View>
</Card>
</View>
);
})}
</ScrollView>
</SafeAreaView>
</ImageBackground>
);
}
This is how my Firestore looks like and I want to retrieve the document id in the circle.
First, you can add the document ID in 'posts' array as shown below:
const posts = querySnapshot.docs.map((d) => ({ id: d.id, ...d.data() }));
setFilteredPosts(posts);
Then you can read post ID when required:
<Icon
name = "thumbs-up"
size = {25}
color = "#fff"
onPress = {() => {
const postRef = doc(db, "posts", post.id);
updateDoc(postRef, {
likeCount: ?? ?? ,
});
}
}
/>
This will give you the particular doc and perform updateDoc query accordingly.
query(collections('collection_name), where(documentId(), '==', 'your_post_id'))
I am getting data from the firebase but able to it on emulator I tried using consloe which workfine
const Getdata = async () => {
await firebase.database().ref(`/orders/${user1.uid}`)
.on("child_added", (snapshot, key) => {
if(snapshot.key) {
console.log('key',snapshot.key);
let grabbedData = snapshot.val().orders;
grabbedData.map((order, i) => {
console.log('order',order.id);
console.log('order',order.avatar);
console.log('order',order.name);
console.log('order',order.price);
console.log('----------------');
});
}
});
}
Getdata();
After modifing the above code as below code nothing is showing to the screen
const Getdata = () => {
let data = firebase.database().ref(`/orders/${user1.uid}`)
.on("child_added", (snapshot, key) => {
// something is wrong with this below statememnt I think
return (
<Card>
<Text>{snapshot.key}</Text>
{
snapshot.val().orders.map((order, i) => {
return (
<TouchableOpacity key={i} onPress={() => {
}}>
<Card>
<View style={styles.user}>
<Image
style={styles.image}
resizeMode="cover"
source={{ uri: order.avatar }}
/>
<View style={{flexDirection:'column', flex: 1}}>
<Text style={styles.name} h4>{order.name}</Text>
<Card.Divider style={{ marginTop: 25}}/>
<View style={{flexDirection:'row', flex: 1,justifyContent: 'space-between'}}>
<Text style={styles.price}>{order.price}</Text>
</View>
</View>
</View>
</Card>
</TouchableOpacity>
);
})
}
</Card>
)
})
return data;
}
and then <Getdata/>
Something I am doing wrong with first return statememnt but dont know what.
Edit I am adding a pic how data is organised
Try this way
const [orders, setOrders] = useState([]); // initially empty
const [key, setKey] = useState(undefined); // undefined empty
const Getdata = async () => {
await firebase.database().ref(`/orders/${user1.uid}`)
.on("child_added", (snapshot, key) => {
if(snapshot.key) {
console.log('key',snapshot.key);
let grabbedData = snapshot.val().orders;
setKey(snapshot.key); // set key here
setOrders(grabbedData); // set orders here to state, it will rerender
}
});
}
useEffect(() => {
Getdata();
});
return (
<Card>
{key && <Text>{snapshot.key}</Text>}
{
orders.map((order, i) => {
return (
<TouchableOpacity key={i} onPress={() => {
}}>
.........
</TouchableOpacity>
);
})
}
</Card>
)
i'm trying to add items to a flatlist via textinput. at the moment i can only add one item and when i try to add a second it just updates the first item, although the data from the textinput should appear below the previous textinput. i have found a few instances of a similar kind of problem and i know that i probably need to add something to my code but i just can't figure out what. below is my code. i would be grateful for any kind of help :)
function FlatlistComponent({ }) {
const listItems = [];
const [arrayHolder, setArrayHolder] = React.useState([]);
const [textInputHolder, setTextInputHolder] = React.useState('');
useEffect(() => {
setArrayHolder(listItems)
}, [])
const joinData = () => {
listItems.push({ name : textInputHolder });
setArrayHolder(listItems);
}
const FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "95%",
backgroundColor: '#00678A',
alignSelf: 'center'
}} />
);
}
// Delete note
deleteNote = id => () => {
const filteredData = arrayHolder.filter(item => item.id !== id);
setArrayHolder({ data: filteredData });
}
return (
<View style={styles.MainContainer}>
<FlatList
data={arrayHolder}
width='100%'
extraData={arrayHolder}
keyExtractor={(item) => item.id}
ItemSeparatorComponent={FlatListItemSeparator}
renderItem={({ item }) => <Text style={styles.item} onPress={deleteNote(item.id)}> {item.name} </Text>}
/>
<TextInput
placeholder='Kirjoita uusi'
onChangeText={data => setTextInputHolder(data)}
style={styles.textInputStyle}
underlineColorAndroid='transparent'
clearTextOnFocus={true}
value={listItems}
/>
<TouchableOpacity onPress={joinData} style={styles.button} >
<Text style={styles.buttonText}> + </Text>
</TouchableOpacity>
</View>
);
}
listItems is always an empty array after component re-rendered, you should concat previous arrayHolder with new item:
const joinData = () => {
setArrayHolder([... arrayHolder, {name: textInputHolder }]);
// or use update function
// setArrayHolder(prev => [...prev, {name: textInputHolder }]);
}