How to render components that rely on firebase async functions? - firebase

I'm unable to have my component render the value from events[0]. I've tried putting the data array in the .then block but the scoping gets screwed up. Since firebase requests are async, how can I make the whole component render only when I receive my data from firebase?
Finalized.js
const Finalized = () => {
let events = [];
firebase
.database()
.ref("events/arts/" + 2 + "/name")
.once("value")
.then((snapshot) => {
events.push(snapshot.val());
});
let data = [{
time: "09:00",
title: "Event 1",
description: <Text>{events[0]}</Text>, // {events[0]} has no value returned
}];
return (
<View style={styles.container}>
<Timeline style={styles.list} data={data} />
</View>
);
};

You can use a state called events instead of the variable.
Here is how to do it...
const [events, setEvents] = useState([]);
const Finalized = () => {
firebase
.database()
.ref("events/arts/" + 2 + "/name")
.once("value")
.then((snapshot) => {
setEvents(snapshot.val());
});
let data = [{
time: "09:00",
title: "Event 1",
description: <Text>{events[0]}</Text>,
}];
return (
<View style={styles.container}>
<Timeline style={styles.list} data={data} />
</View>
);
};

Related

How do I pass a specific object from a FlatList to a function so I could delete it from Firestore?

I have a watchlist of bookings for movies and when I click on a button for one of the FlatList rendered components I'd like to pass the object to the delete function and then to remove it from Firestore. I'm kind of stuck on how to do this. This is what I have so far:
const WatchList = () => {
const uid = auth.currentUser.uid;
const docRef = doc(db, 'users', uid);
const [user, setUser] = useState({});
const [watched, setWatched] = useState(true);
const [text, setText] = useState('Watched movies');
const [filteredBookings, setFilteredBookings] = useState(bookings);
const bookingsRef = collection(db, "booking");
const [bookings, setBookings] = useState({});
useEffect(() => {
getUser();
getBookings();
},[])
const getUser = async () => {
const snap = await getDoc(docRef)
setUser({user, ...snap.data()})
}
const getBookings = async () => {
const q = query(bookingsRef, where("users","array-contains",auth.currentUser.uid));
const unsubscribe = onSnapshot(q, (querySnapshot) => {
const a = [];
querySnapshot.forEach((doc) => {
a.push(doc.data());
});
setBookings(querySnapshot.docs);
});
}
const deleteBooking = (item) => {
console.log(item.data.().title)
}
return (
<View>
<View>
<Text>{text}</Text>
</View>
<FlatList
data = {filteredBookings}
numColumns = {1}
renderItem = {({item}) => (
<View>
<View>
<Text>{item.data().movie}</Text>
<Text>{item.data().day} - {item.data().showtime}</Text>
</View>
<View>
<TouchableOpacity onPress = {() => {deleteBooking(item)}}>
<Text>Delete</Text>
</TouchableOpacity>
</View>
</View>
)}
/>
</View>
)
}
export default WatchList`
I've been trying to pass an item to display it in the console log to see if I got the right one first, but it's not working, so I'd really appreaciate some pointers. Thank you!
In your delete button's onPress method which you have defined as deleteBooking() you will get index also. you can rewrite as deleteBooking(item,index). and now in your definition use splice method of array. e.g. arrayName.splice(index,1) this will delete the record of given index.
For your array it should be like bookings.splice(index,1). Here 1 represents how many record you want to delete from given index.
Let me know doest it works for you or not.

React Native Pressable has to be clicked twice to be executed

I am using react native and firebase v9. When I click to add a chatroom, an alert prompt pops up, the user enters a room name and firebase should automatically roll it out to the UI. But what happens is when the modal is closed, I then have to click the add button again and then the chat room will roll out. How can I fix this so when the user enters a name, presses ok it automatically shows on the UI? Thanks!
My state:
const [rooms, setRooms] = useState([]);
const [input, setInput] = useState("");
Here is my JSX:
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
<Pressable onPress={handleAddRoom}>
<View style={styles.addRoom}>
<MaterialCommunityIcons
name="plus-circle"
color="black"
size={42}
/>
<Text style={styles.nameText}>Add</Text>
</View>
</Pressable>
Here are my functions for uploading to firebasev9:
const handleAddRoom = () => {
Alert.prompt("Add New Chat Room", "Please enter a name", [
{ text: "CANCEL", onPress: null },
{ text: "OK", onPress: (text) => setInput(text) },
]);
if (input) {
const colRef = collection(db, "rooms");
addDoc(colRef, {
image: inputData,
roomName: input,
});
}
setInput("");
};
const fetchRooms = async () => {
try {
const colRef = query(collection(db, "rooms"), orderBy("roomName", "asc"));
onSnapshot(colRef, (snapshot) => {
setRooms(
snapshot.docs.map((doc) => ({
id: doc.id,
roomName: doc.data().roomName,
image: doc.data().image,
}))
);
});
} catch (err) {
console.log(err);
}
};
useEffect(() => {
fetchRooms();
}, []);

Firebase react native flatlist keyExtractor

Im displaying some firebase data in a flatlist and im having trouble with the keyExtractor, I keep having the error:
undefined is not an object (evaluating "item.id")
I have added an id field to all my data in firebase and made sure they were a string but it's still not recognizing it as an id.
function Squad() {
const [gk, setGk] = useState([]);
useEffect(() => {
db.collection('squad').orderBy('position').get().then(snapshot => {
const gkData = snapshot.map(doc => {
const playerObject = doc.data();
return { name: playerObject.name, number: playerObject.number, id: playerObject.id };
});
setGk(gkData);
console.log(setGk);
});
}, []);
const Item = ({ name, number }) => (
<View style={styles.item}>
<Text style={styles.itemText}>{number} - {name}</Text>
</View>
);
const renderItem = ({ item }) => (
<Item name={item.name} number={item.number} />
)
return(
<View>
<View style={globalStyles.bar}>
<Text style={globalStyles.barText}>goalkeeper</Text>
</View>
<FlatList
data={setGk}
renderItem={renderItem}
keyExtractor={item => item.id}
/>
</View>
)
}
The data you are passing into the Flatlist is the setter function! You want to pass in ‘gk’ not ‘setGk’

mapping firebase read to props react-native

this might not be an issue with the mapping itself but i've read some data from my firebase realtime database into my sate and i'm trying to pass it as props then map it in a subsequent component.
I am getting the following error, I am using an android emulator:
TypeError: undefined is not a function (near ...'this.props.notes.map'...)
app.js (where I update the state)
state = {
loggedin: false,
notes: [
{
id: 1,
text: "mow the lawn",
author: "dean",
time: "10am"
},
{
id: 2,
text: "feed the dog",
author: "sam",
time: "2pm"
}
]
}
//passing props to notes component
<Notes style={styles.notes} notes={this.state.notes} />
updateNotes = async () => {
console.log("grabbing new notes");
const snapshot = await firebase.database().ref('Users/Notes').once('value');
console.log(snapshot.val())
this.setState({ notes: snapshot.val() });
};
my Notes component where I map the props
renderCondition =()=>{
if(this.state.Deleted === false){
return(
<View>
{this.props.notes.map(note => (
<View
style={styles.note}
key={note.author}
id={note.id}
>
<Text style={styles.noteHeader}>{note.author}</Text>
<Text style={styles.noteText}>{note.text}</Text>
<Text style={styles.noteTime}>{note.time}</Text>
<Button title= 'X' onPress={() => this.setState({Deleted:true}) }></Button>
</View>
))}
</View>
)
}
return(
<View>
<Text>are you sure you want to delete this note?</Text>
<Button title="Yes"></Button>
<Button onPress ={()=>{this.setState({Deleted:false})}} title="No"></Button>
</View>
)
}
render() {
return (
<View>
{this.renderCondition()}
</View>
);
}
You have to check whether the notes have been passed down or whether they are undefined or null. JavaScript is not going to map an undefined object.
Try the following code:
{this.props.notes && this.props.notes.map(note => (
// do stuff with each note...
))}
This will run the .map function only if the notes are neither undefined nor null.
So my issue was that I needed to update my notes state by making sure it was an array. I had mistakenly simply updated it as an object and then attempted to map the object. Here is my solution.
from this
updateNotes = async () => {
console.log("grabbing new notes");
const snapshot = await firebase.database().ref('Users/Notes').once('value');
console.log(snapshot.val())
this.setState({ notes: snapshot.val() });
};
to this
updateNotes = async () => {
console.log("grabbing new notes");
const snapshot = await firebase.database().ref('Users/Notes').once('value');
console.log(snapshot.val())
this.setState({ notes: [snapshot.val()] });
};

Firebase gets undefined from reducer, but state from reducer logs as a boolean

I have a simple Switch component that toggles back and forth between true and false. I've wired it up in my actions to Set this boolean in a table in Firebase. Firebase says that 'first argument contains undefined in property'. However, when I log that specific prop, I can see that it logs as true and false as I switch it. The prop is 'sold'. All other props are Set just fine.
Here's the action creator that tries to Set to Firebase:
export const entrySave = ({ make, model, year, uid, image, sold }) => {
const { currentUser } = firebase.auth();
return (dispatch) => {
firebase.database().ref(`/users/${currentUser.uid}/entries/${uid}`)
.set({ make, model, year, image, sold })
.then(() => {
dispatch({ type: ENTRY_SAVE_SUCCESS });
Actions.main({ type: 'reset' });
});
};
};
Here's the action creator that updates the change of this.props.sold:
export const entryUpdate = ({ prop, value }) => {
return {
type: ENTRY_UPDATE,
payload: { prop, value }
};
};
Here's the Switch:
console.log(this.props.sold);
//both logging 'this.props.sold' and the redux debugger show that
//this.props.sold is toggling between true and false
return (
<View>
<View style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', height: 66 }}>
<View>
<Text style={styles.switchLabelStyle}>Mark As Sold</Text>
</View>
<View style={{ paddingRight: 20 }}>
<Switch
tintColor='#a6a7a8'
onValueChange={value => this.props.entryUpdate({ prop:
'sold', value })}
value={this.props.sold}
/>
</View>
</View>
And lower down:
const mapStateToProps = (state) => {
const { make, model, year, sold } = state.entryForm;
return { make, model, year, sold };
};
export default connect(mapStateToProps, { entryUpdate })(EmployeeForm);
And here's the reducer:
const INITIAL_STATE = {
make: '',
model: '',
year: '',
image: '',
sold: false,
uid: '',
loading: false;
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case ENTRY_UPDATE:
return { ...state, [action.payload.prop]: action.payload.value };
case ENTRY_CREATE:
return INITIAL_STATE;
case ENTRY_SAVE_SUCCESS:
return INITIAL_STATE;
case ENTRY_CLEAR:
return INITIAL_STATE;
Anyone see what I'm missing? Thanks in advance!
Silly me...I forgot to include 'sold' as a prop in the parent component's mapStateToProps function.
const mapStateToProps = (state) => {
const { make, model, year, image, sold } = state.entryForm;
return { make, model, year, image, sold };
};

Resources