in my case I want to implement the onSnapshot function for getting realtime updates, so here's my sample code it's working now:
db.collection("Cities").onSnapshot((snapshot) => {
snapshot.docs.forEach((doc) => {
console.log(doc.data());
});
});
But now, how can I implement it to the following code
getMyInfo = () => {
db.collection("Cities")
.limit(8)
.get()
.then((docs) => {
if (!docs.empty) {
let AllCities = [];
docs.forEach(function (doc) {
const city = {
id: doc,
...doc.data(),
};
AllCities.push(city);
});
this.setState(
{
cities: AllCities,
},
() => {
this.setState({
isLoaded: true,
});
}
);
}
});
};
Thanks
You are saving all your cities into your state. Use it to add or update the new (or updated) cities.
Maybe you can try something like that :
db.collection("Cities").onSnapshot((snapshot) => {
snapshot.docs.forEach((doc) => {
const updatedCity = doc.data();
const cities = [...this.state.cities];
const index = this.state.cities.findIndex(c => c.id === updatedCity.id);
if (index >= 0) {
cities[index] = updatedCity;
} else {
cities.push(updatedCity);
}
this.setState({ cities });
});
});
Related
I'm trying to arrange my chats gotten from firebase into and object.
I get the chats then try to arrange it into an object which contains a the chat object and the receiver.
This code arranges the chats but only does so after I have refreshed the expo app. I checked the internet and saw that it was because users and and chats are states and they had not been set by then.
useEffect(() => {
const chatsSub = onSnapshot(collection(firestore, "Chats"), querySnapshot => {
const data = []
querySnapshot.forEach((doc) => {
data.push(doc.data())
});
console.log("Chats: ", data);
setChats(data)
const list = []
chats.forEach(chat => {
if (chat.members.includes(userId)) {
chat.members.forEach(y => {
if (y !== userId) {
console.log("receiver: " + y)
users.forEach(z => {
if (z.id === y) {
console.log(z)
list.push({
chat: chat,
acc: z,
user: user
})
}
})
console.log(list)
}
})
}
})
setUserChats(list)
console.log("Users Chats: " + userChats)
});
}, [])
But when I try to get the users and chats at the time I'm trying to arrange the user chats. But this code does not work. It just throws an error instea
useEffect(() => {
const list = []
doc.data().chatList.forEach(chatId => {
const chatSub = onSnapshot(doc(firestore, "Chats", chatId), (doc1) => {
doc1.data().members.forEach(member => {
if (member !== userId){
const userSub = onSnapshot(doc(firestore, "Users", member), (doc2) => {
list.push({
chat: doc1.data(),
acc: doc2.data()
})
})
}
})
})
})
setUserChats(list)
}, [])
I don't know if it's because I cant have an onSnapshot inside another onSnapshot
You can build the chat list once both the state values have been updated.
Move the logic to build the chat list into an useEffect hook and add dependencies for chat and user states.
Refer the below code.
useEffect(() => {
if (chats.length && users.length) {
const list = [];
chats.forEach((chat) => {
if (chat.members.includes(userId)) {
chat.members.forEach((y) => {
if (y !== userId) {
console.log("receiver: " + y);
users.forEach((z) => {
if (z.id === y) {
console.log(z);
list.push({
chat: chat,
acc: z,
user: user
});
}
});
console.log(list);
}
});
}
});
setUserChats(list);
}
}, [chats, users]);
I am using React/Redux.
The main issue is that when i use Promise then component is not re-rendered, whereas the code is working fine when promise code is not used.
Action Creator
const updateColor = colorobj => {
return dispatch =>
new Promise(function(resolve, reject) {
dispatch(fetchColorBegin());
axios
.post(config.APIURL.color.update, colorobj)
.then(response => {
const data = response.data;
if (data.errorno !== 0) {
dispatch(fetchColorFailure(data.errormsg));
reject(data.errormsg);
} else {
dispatch(updateColorSuccess(colorobj));
resolve('Color Updated');
}
})
.catch(error => {
dispatch(fetchColorFailure(error.message));
reject(error.message);
});
});
};
Reducer
case UPDATE_COLOR_SUCCESS:
const todoIndex = state.data.findIndex(todo => todo.id === action.payload.id);
return update(state, {
loading: { $set: false },
data: { [todoIndex]: { $merge: action.payload } },
error: { $set: null}
});
Component
the state is updated but the component is not updated.
const handleEditOk = values => {
let colorobj = {
id: state.updateData.id,
colorname: values.colorname,
colorcode: values.colorcode,
};
dispatch(updateColor(colorobj))
.then(response => {
message.success(response);
onCancel();
})
.catch(error => {
message.error(error);
});
};
The component update itself only on commenting the promise code.
The problem now is that it is not showing success/failure message.
const handleEditOk = values => {
let colorobj = {
id: state.updateData.id,
colorname: values.colorname,
colorcode: values.colorcode,
};
dispatch(updateColor(colorobj))
// .then(response => {
// message.success(response);
// onCancel();
// })
// .catch(error => {
// message.error(error);
// });
};
Kindly suggest.
I'm new to Firebase Realtime Database, and i'm trying to implement a search field that allow users to search for other users and view their profiles.
The Problem Is:
I want to make the search realTime(on each input change).but whenever a new request's sent, the old request is still working in the backend which's causing unexpected behavior,i've wrapped this functionality in a useEffect Hook,old sideEffects has to be cleaned up to make the query results predictable,how can i abort the previous request.
useSearchOwner Custom Hook:
const useSearchOwner = () => {
const [{ SearchValue, SearchResult, Search }, dispatch] = useReducer(
reducer,
{
SearchValue: "",
SearchResult: "",
Search: false,
}
);
const isFirstRender = useRef(true);
const onChangeHandler = (e) =>
dispatch({
type: ACTIONS.UPDATE_SEARCH_VALUE,
payload: { searchValue: e.target.value },
});
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
dispatch({ type: ACTIONS.START_SEARCHING });
const DispatchQueryByResult = async () => {
const ArrayOfOwners = await FirebaseUtilityInstance.SearchOwnerResult(
SearchValue
);
dispatch({
type: ACTIONS.UPDATE_SEARCH_RESULT,
payload: { searchResult: ArrayOfOwners },
});
dispatch({ type: ACTIONS.STOP_SEARCHING });
return () => {
FirebaseUtilityInstance.SearchOwnerCleanup();
};
};
DispatchQueryByResult();
}, [SearchValue]);
useEffect(() => {
console.log(SearchResult);
}, [SearchResult]);
return {
onChangeHandler: onChangeHandler,
Query: SearchValue,
QueryResult: SearchResult,
isSearching: Search,
};
};
Firebase Method To Do Query:
SearchOwnerResult = async (Query) => {
const { firstName, lastName } = getFirstNameAndLastName(Query);
let ArrayOfOwners = [];
await this.Database()
.ref("users")
.orderByChild("UserType")
.equalTo("owner")
.once("value", (snapshot) => {
const OwnersContainer = snapshot.val();
const keys = Object.keys(OwnersContainer);
for (let i = 0; i < keys.length; i++) {
const CurrentOwner = OwnersContainer[keys[i]];
if (
CurrentOwner.FirstName === firstName ||
CurrentOwner.LastName === lastName
) {
ArrayOfOwners.push(OwnersContainer[keys[i]]);
}
}
});
return ArrayOfOwners;
};
I have the following useEffect set up to fetch some data from firebase to populate a flatlist
useEffect(() => {
return db
.collection('accounts')
.doc(currentAccountId)
.collection('shops')
.doc(currentShopId)
.collection('sensors')
.onSnapshot((querySnapshot) => {
const list = [];
querySnapshot.forEach((doc) => {
const { sensorReadingsId } = doc.data();
// This works, but doesn't load the additional data I need.
// list.push({ ...{ id: doc.id }, ...doc.data() });
// This doesn't seem to load anything.
db.collection('sensors')
.doc(sensorReadingsId.toString())
.onSnapshot((documentSnapshot) => {
list.push({ ...{ id: doc.id }, ...doc.data(), ...documentSnapshot.data() });
});
});
setSensorsData(list);
console.log(sensorsData);
setLoading(false);
});
}, [currentAccountId, currentShopId]);
Here's the problem: my flatlist doesn't load anything. When I initially load the screen, that console.log outputs an empty array. If I force it to re-render by saving the screen file, the array is correctly populated.
As noted in that comment, if I just use list.push({ ...{ id: doc.id }, ...doc.data() }); instead of db.collection('sensors')... it loads the sensors fine, but without the additional data that I need.
How do I resolve this? Thanks in advance!
Solution: As #Rajitha Udayanga has pointed out, checking the length of the first querysnapshot against the list array is a great way to check whether or not all the data has been loaded. Here's the working code (note the conditional that setSensorsData has been moved into):
useEffect(() => {
return db
.collection('accounts')
.doc(currentAccountId)
.collection('shops')
.doc(currentShopId)
.collection('sensors')
.onSnapshot((querySnapshot) => {
const list = [];
querySnapshot.forEach((doc) => {
const { sensorReadingsId } = doc.data();
db.collection('sensors')
.doc(sensorReadingsId.toString())
.onSnapshot((documentSnapshot) => {
list.push({ ...{ id: doc.id }, ...doc.data(), ...documentSnapshot.data() });
// Fix is here:
if (querySnapshot.docs.length === list.length) {
setSensorsData(list);
console.log(sensorsData);
setLoading(false);
}
});
});
});
}, [currentAccountId, currentShopId]);
Disclaimer: I have zero experience with React.
In Angular I deal with such issues using RxJS's combineLatest.
However, you can try this one:
useEffect(() => {
return db
.collection('accounts')
.doc(currentAccountId)
.collection('shops')
.doc(currentShopId)
.collection('sensors')
.onSnapshot((querySnapshot) => {
const list = [];
const useValues = () => {
if (list.some(value => value === undefined)) {
return; // not everything has loaded yet - abort
}
setSensorsData(list);
console.log(sensorsData);
setLoading(false);
}
querySnapshot.forEach((doc, index) => {
list.push(undefined);
const { sensorReadingsId } = doc.data();
db.collection('sensors')
.doc(sensorReadingsId.toString())
.onSnapshot((documentSnapshot) => {
list[index] = ({ id: doc.id, ...doc.data(), ...documentSnapshot.data() });
useValues();
});
});
});
}, [currentAccountId, currentShopId]);
And don't forget to detach the listeners when you don't need them anymore.
useEffect(() => {
return db
.collection('accounts')
.doc(currentAccountId)
.collection('shops')
.doc(currentShopId)
.collection('sensors')
.onSnapshot((querySnapshot) => {
const list = [];
querySnapshot.forEach((doc) => {
const { sensorReadingsId } = doc.data();
db.collection('sensors')
.doc(sensorReadingsId.toString())
.onSnapshot((documentSnapshot) => {
const newData = {
...{ id: doc.id },
...doc.data(),
...documentSnapshot.data()
};
const itemIndex = list.findIndex(item => item.id ===
newData.id);
if (itemIndex !== -1) {
list[itemIndex] = newData;
} else {
list.push(newData);
}
// do it like thi
if (querySnapshot.docs.length === list.length) {
setSensorsData(list);
console.log(sensorsData);
setLoading(false);
}
});
});
});
}, [currentAccountId, currentShopId]);
I'm using ReactJS as a javascript library and I am getting the data in componentDidMount() using axios. These received data must be taken again every 60 seconds. What is the most efficient and good way to do this?
componentDidMount() {
const newGteeChartSeries = [];
const newGteeChartCategories = [];
const newmultiSelectOption = [];
axios.get(`http://www.xxxxxxx:xxxx/api/groupdata`).then(res => {
this.state.gteeChartSeries.map(() => {
const data = [];
res.data.map((item, index) => {
data.push(item.gtee);
newGteeChartCategories.push(item.isyeri);
newmultiSelectOption.push({ id: item.id, isyeri: item.isyeri });
});
newGteeChartSeries.push({ name: "GTEE", data });
});
this.setState({
teeTableData: res.data,
gteeChartSeries: newGteeChartSeries,
multiSelectOptions: newmultiSelectOption,
gteeChartoptions: {
...this.state.options,
xaxis: {
categories: newGteeChartCategories
}
}
});
});
}
One way of going about it is to move the data fetching logic to a separate method and create an interval that will invoke this method every 60 seconds.
Make sure you store the number returned from setInterval on the component instance so you can use clearInterval in componentWillUnmount.
Example
class MyComponent extends React.Component {
interval = null;
componentDidMount() {
this.interval = setInterval(this.getData, 60000);
this.getData();
}
componentWillUnmount() {
clearInterval(this.interval);
}
getData = () => {
const newGteeChartSeries = [];
const newGteeChartCategories = [];
const newmultiSelectOption = [];
axios.get(`http://www.xxxxxxx:xxxx/api/groupdata`).then(res => {
this.state.gteeChartSeries.forEach(() => {
const data = [];
res.data.forEach((item, index) => {
data.push(item.gtee);
newGteeChartCategories.push(item.isyeri);
newmultiSelectOption.push({ id: item.id, isyeri: item.isyeri });
});
newGteeChartSeries.push({ name: "GTEE", data });
});
this.setState({
teeTableData: res.data,
gteeChartSeries: newGteeChartSeries,
multiSelectOptions: newmultiSelectOption,
gteeChartoptions: {
...this.state.options,
xaxis: {
categories: newGteeChartCategories
}
}
});
});
};
}
I would suggest abstracting the api request into its own function
componentDidMount(){
setInterval(yourApiCallFn(),60000)
}
You can wrap all in a function.
Call that function in ComponentDidMount(), and use setInterval(myFunction(), 60000) to call that function every 60 seconds
Below works without syntax error.Call that function without parenthesis
``componentDidMount() {
this.interval = setInterval( this.props.Details, 6000);
this.props.Details()
}
componentWillUnmount() {
clearInterval(this.interval);
}``
Well let's do that with a normal javascript setTimeInterval.
let intervalLoop = null; // a class variable
componentDidMount() {
const newGteeChartSeries = [];
const newGteeChartCategories = [];
const newmultiSelectOption = [];
this.intervalLoop = setInterval(()=>{
axios.get(`http://www.xxxxxxx:xxxx/api/groupdata`).then(res => {
this.state.gteeChartSeries.map(() => {
const data = [];
res.data.map((item, index) => {
data.push(item.gtee);
newGteeChartCategories.push(item.isyeri);
newmultiSelectOption.push({
id: item.id,
isyeri: item.isyeri
});
});
newGteeChartSeries.push({
name: "GTEE",
data
});
});
this.setState({
teeTableData: res.data,
gteeChartSeries: newGteeChartSeries,
multiSelectOptions: newmultiSelectOption,
gteeChartoptions: {
...this.state.options,
xaxis: {
categories: newGteeChartCategories
}
}
});
});
}, 60000);
}
// need to cleanup the timeinterval whenever we destroy the component
componentWillUnmount(){
clearInterval(this.intervalLoop)
}