I am trying to conditionally update a document in my react native project and I have not fully understood how that works in react native or if it is impossible. I would really appreciate if someone can explain me the logic.
below is my code i have been playing around with
useEffect( () => {
firebase.firestore().collection('orders').doc(firebase.auth().currentUser.uid)
.collection('pending').where('price', '==', 55000).onSnapshot((snapshot) => {
let items = snapshot.docs.map(doc => {
const data = doc.data();
const id = doc.id;
setItemId(id)
setBrand(data.brand)
setColor(data.color)
setDescription(data.description)
setDownloadUrl(data.downloadUrl)
setPrice(25000)
return{id, ...data }
})
})
firebase.firestore().collection('orders').doc(firebase.auth()
.currentUser.uid)
.collection('pending').doc(itemId).set({
price,
brand,
color,
description,
downloadUrl,
})
So what i did in this code was to run through all the data in the document and look for any that has it's price == 55000 then re-write it's data changing only the old price to the new one (please pardon me if it is not clear). But what this code does is that it deletes the old data and create a new document with empty data.
You could just simply iterate the result from snapshot and update the field from the document reference. See sample code below:
firebase.firestore().collection('orders').doc(firebase.auth().currentUser.uid)
.collection('pending').where('price', '==', 55000)
.onSnapshot((snapshot) => {
snapshot.forEach((doc) => {
doc.ref.update({'price': 25000})
});
});
For more information you may check out this documentations:
DocumentReference
Update a document
Related
I am implementing the "favorites" logic in a project where there are some cards showing some info about songs, and a heart icon shows red if that song is in the user favorites or empty if it is not.
I am using Supabase as a backend (SQL) and I already have a "profiles" table, a "songs" table and a "favorites" table with two foreign keys: profile_id and song_id.
In the project I am using RTKQuery and I have already configured the endpoint to obtain the data for the favorites which is like:
endpoints: (builder) => ({
getFavorites: builder.query({
queryFn: async (id) => {
const {data, error} = await supabase
.from('favorites')
.select(`users_id,songs_id (*)`)
.eq('users_id',id)
return { data, error }
},
providesTags: ["Favorites"]
}),
Then I get the data with:
const { data, isSuccess } = useGetFavoritesQuery( id );
I am calling this hook from the component mad for each card, which already have the props. So I can check if the prop "song.id" is in some of the ids inside the "favorites" object.
But the problem I am facing is because the data that I really need is the nested object inside data, and each one of the nested objects have the id that I would use to check if it is === to song.id of the component I am calling from.
I have tried several things but I don't know if I am doing fine. This is one solution that I have tried. First I iterate over data to obtain the object "favorites":
let favorites;
if (isSuccess){
data.forEach(element => {
favorites = element.songs_id;
})
}
And then the boolean for the heart red or empty:
const favoriteIconColor = useMemo(() => {
if (Object.values(favorites).some(item=> item.id === song.id)){
return true
}
} , [favorites, song]);
But that is not working. I am thinking about creating just an array with all of the ids in the first iteration, but I am not sure if I am overcomplicating the issue.
I have also considered that maybe there is a way to obtain the data as I need with the query itself, but not sure about it. Any advice?
OK, say I have an initial state in our Redux store that looks like this:
const initialState = {
userReports: [],
activeReport: null,
}
userReports is a list of reports. activeReport is one of those reports (the one that is actively being worked with).
I want the active report to point to one in the array. In other words, if I modify the active report, it would modify one in the userReports array. This means, the two objects must point to the same memory space. That's easy to set up.
The alternative to this approach would be to copy one of the reports that is in the userReports array and set it as the active report (now it has a different memory address). The problem is now, when I edit the activeReport, I also have to search through the array of userReports, find the report that resembles the active report and modify it there too. This feels verbose.
Here is the question:
Would it be bad practice to have the activeReport point to a report in the array (same object). When I want to change the report I could do something like this (example is using redux thunk):
export const updateReport = (report) => async (dispatch, getState) => {
try {
const report = getState().reports.activeReport
// modify the active report here
report.title = "blah blah blah"
dispatch({ type: ACTIONS.UPDATE_REPORT, payload: report })
} catch (error) {
console.log(`ERROR: ${error.message}`)
}
}
And in my reducer:
case ACTIONS.UPDATE_REPORT:
return { ...state, activeReport: action.payload }
as you can see, after updating the report I still return a "new version" of that report and set it as active, but this approach also updates the report in the userReports array because they point to the same memory address.
I would say thats not ideal, do the reports have id's? If they do I would rather hold the userReports in an object with keys being the id's, then active report can just be an id and renamed to activeReportId so you can fetch the activeReport with userReports[activeReportId]
You also asked for reasons:
So firstly any screen that looks at userReports wont rerender because the reports aren't being reassigned.
Secondly if someone later wants to update those screens they will reassign userReports which could cause problems.
Thirdly its an unusual pattern which is a huge no no for redux. The point of redux is that it has a very obvious pattern so when you add things to it you don't have to think and can just make changes with confidence.
Your activeReport should not be pointing to an object in the userReports array, but rather it should be an id of the report, which the user is currently working on. Each of the report in the userReports will have a unique id field to identify the report - this would be helpful when rendering in react - this id field can be used as key.
Then your action creator/dispatcher will look like this:
export const updateReport = (updatedReport) => async (dispatch, getState) => {
dispatch({ type: ACTIONS.UPDATE_REPORT, payload: updatedReport });
}
You will call this on change in your component:
const onTitleChangeHandler = (e) => {
var newTitle = e.target.value;
// you will get the userReports and activeReport from props or by using some redux selector, also you will need to get dispatch and getState from redux
var activeReportObj = userReports.filter((r) => r.id === activeReport)[0];
updateReport({ title: newTitle, ...activeReportObj })(dispatch, getState);
}
Lastly, your reducer will be:
case ACTIONS.UPDATE_REPORT:
var newUserReports = state.userReports.map((r) => {
if (r.id === state.activeReport) {
return action.payload;
}
return r;
});
return { newUserReports, ...state };
I have a function for when clicking a button increase the contents of a list.
Content is removed from an API by the following code:
const [data, setData] = useState();
const [maxRange, setMaxRange] = useState(2);
const getAPIinfo = ()=>{
GetEvents(maxRange, 0).then((response) => response.json())
.then(result_events => {
const events = result_events;
setData({events:events});
}).catch(e => setData({events:events}));
}
And my function is this:
const buttonLoadMore = ({data,type}) =>{
setMaxRange(prevRange => prevRange + 4);
data = data.slice(0,maxRange);
}
what I'm not able to do is update the maxRange value of the API query to increase the list...
this function should be heavily refactored:
const buttonLoadMore = ({data,type}) =>{
setMaxRange(prevRange => prevRange + 4);
data = data.slice(0,maxRange);
}
when you use maxRange here, you are setting new state, while the function itself ir running, the state is not instantly updated, buttonLoadMore is a function in a particular time. it cannot get new maxRange instantly, while running buttonLoadMore does that make sense? Also you cannot update data state just like a regular variable by assigning new variable using = operator, you should refactor this function to something like this:
const buttonLoadMore = ({data})=> {
const newMaxRange = maxRange + 4;
setMaxRange(newMaxRange);
const newData = {events: [...data.events.slice(0, newMaxRange)]};
setData({...newData})
}
also you will get bug here. since your getAPIinfo is setting data state to an object {events: events}. I took the liberty and tried refactoring it here.
There is also a bug in your getAPIinfo in line }).catch(e => setData({events:events})); the events variable you declared in .then function cannot be reached here. It is simply out of scope. unless you know that .catch resolves into data, you will get an error in this line.
take a look at this example here:
const promiseFunction = ()=>{
return new Promise<string>((resolve)=>resolve('i like coca cola'))
}
const getter = () => {
promiseFunction()
.then(response => {
const thenVariable = response;
console.log(thenVariable) // i like coca cola
})
.catch(error=>{
console.log(thenVariable) // Error:Cannot find name 'thenVariable'.
})
}
as you can see .catch() is in different scope than .then() will not be available outside so events cannot be reached by .catch function.
Usually you would use catch for error handling. Maybe show a line on screen, that error has accoured, and data cannot be fetched at this time. etc. There's a very good book that explains all these concepts in detail here: https://github.com/getify/You-Dont-Know-JS
I would strongly recommend for you to switch to typescript because your code is crawling with bugs that should be easily avoided just by type checking, and adding eslint configurations.
First of: I'm a beginner at Vue.js/APIs so I hope my question is not too stupid (I may not be seeing the obvious) :)
So,
Using Vue.js I'm connecting to this API and want to track the history of each crypto-currencies (no issues with getting any data from the API).
Currencies information are accessible using a URL :
https://api.coinranking.com/v2/coins
And history is accessible using another :
https://api.coinranking.com/v2/coin/ID_OF_THE_COIN/history
As you can see the second url needs the id of the specific currency which is available in the first one.
I would like to find a way to make only 1 get request for all currencies and their history rather than having to make as many requests as available currencies there are (about 50 on this API), I've tried several things but none has worked yet (for instance using the coin url and storing ids of the currencies in a table then using the history url and modifying it with the ids of the table but hit a wall) .
Here's the axios get request I have for the moment for a single currency:
const proxyurl = "https://cors-anywhere.herokuapp.com/"
const coins_url = "https://api.coinranking.com/v2/coins"
const history_url = "https://api.coinranking.com/v2/coin/Qwsogvtv82FCd/history"
//COINS DATA
axios
.get(proxyurl + coins_url, {
reqHeaders
})
.then((reponseCoins) => {
// console.log(reponseCoins.data)
this.crypto = reponseCoins.data.data.coins;
})
.catch((error) => {
console.error(error)
})
//GET ALL COINS UUIDs
axios
.get(proxyurl + coins_url, {
reqHeaders
})
.then((reponseUuid) => {
this.cryptoUuidList = reponseUuid.data.data.coins;
//access to each crypto uuid:
this.cryptoUuidList.forEach(coinUuid => {
console.log("id is: " + coinUuid.uuid)
//adding uuids to table:
this.coinsUuids.push(coinUuid.uuid);
});
})
.catch((error) => {
console.error(error)
})
// COIN HISTORY/EVOLUTION COMPARISON
axios
.get(proxyurl + history_url, {
reqHeaders
})
.then((reponseHistory) => {
//get data from last element
const history = reponseHistory.data.data.history
this.lastItem = history[history.length-1]
// console.log(this.lastItem)
this.lastEvol = this.lastItem.price
// console.log(this.lastEvol)
//get data from previous element:
this.previousItem = history[history.length-2]
this.previousEvol = this.previousItem.price
})
.catch((error) => {
console.error(error)
})
I probably forgot to give some info so let me know and will gladly share if I can
cheers,
I took a look at the API, they do not seem to give a way for you to get everything you need in one request so you will have to get each coin history separately.
However, I do se a sparkline key in the returned data, with what seems to be a few of the latest prices.
I do not know your projects's specifics but maybe you could use that for your initial screen (for example a coins list), and only fetch the full history from the API when someone clicks to see the details of a coin.
I am new to React Native. I am trying to make an app of my own to try out the different things that I learnt and also get to know new things and one such thing that I came across and is giving me a hard time is the following issue:
I have an API which gives me certain data about an item. The properties of the item are listed in the API like "sizeofitem" , "nameofitem" or "itemacategory". Now there are multiple items for different items and not all properties are present in each item. What I was trying to achieve is to somehow map these properties in the following manner:
If let's say "sizeofitem", should become "Size of Item", "nameofitem" should become "Name of Item". Now these properties are different of all the items so for example, sizeofitem might be in one item detail list but might not be in another, but I have all the properties that are can be there. Can someone help me how to do this?
Till now I have the following:
const [itemDtl , setItemDtl] = useState([]);
const getItemInfo = async (id) => {
try{
const response = await api.get(`myAPI/${id}`);
setItemDtl(response.data.obj.itemutils);
}catch(err){
console.log(err);
}
}
let arr = [];
for(let i in itemDtl){
arr.push(itemDtl[i].util_type);
}
console.log(arr);
useEffect(() => {
getItemInfo(id);
})
arr array has whatever the properties where listed for the item in the API i.e. [sizeofitem, nameofitem , etc].
I want an array to have [Size of Item, Name of Item , etc].
Basically just, to sum up, I want to rename the list of properties that can be there for when whatever property comes up is then stored in an array with the mapped string I have given, so for example if an item has 'sizeofitem : 50', I want it to be stored as "Size of item" so that I can show that on the screen. And there are like a total of 5 properties that can exist for an item so I can code it somewhere maybe like sizeofitem : 'Size Of Item' so that when sizeofitem property is top be shown on the screen I can use this and show Size of Item on the screen.
try this:
const [itemDtl , setItemDtl] = useState([]);
const getItemInfo = async (id) => {
try{
const response = await api.get(`myAPI/${id}`);
let arr = [];
for(let i in itemDtl){
arr.push(itemDtl[i].util_type);
}
setItemDtl(arr);
}catch(err){
console.log(err);
}
}
useEffect(() => {
getItemInfo(id);
})