Appending data to AsyncStorage - javascript

I am trying to append an Object to an existing array of objects in AsyncStorage, however not being successful. Any help is appreciated!
Current code:
const [storageItems, setStorageItems] = useState([]);
const handleHabitCreation = async () => {
setLoading(true);
const newHabit = {
name: name,
color: updatedColor,
days: daysCount,
times: timesCount,
reminder: selectedDate,
description: description,
};
try {
const jsonValue = await AsyncStorage.getItem('#habit');
setStorageItems(jsonValue);
} catch (error) {
console.error(error);
}
const stringifiedHabit = JSON.stringify(newHabit);
setStorageItems([...storageItems, stringifiedHabit]);
try {
await AsyncStorage.setItem('#habit', JSON.stringify(storageItems));
setTimeout(() => {
setLoading(false);
navigation.pop(3);
}, 2500);
} catch (error) {
console.error(error);
}
};

The proplem is that you are not parsing the stringified json data. You need to parse it to perform any modification. Save the data in the state as parsed json and srtingify it just before saving it to the async storage. Try this code:
try {
const jsonValue = await AsyncStorage.getItem('#habit');
// saving parsed json
const data = JSON.parse(jsonValue)
setStorageItems(data);
} catch (error) {
console.error(error);
}
// state depends on previous state
setStorageItems((prevState) => [...prevState, newHabit]);
or to make shorter
try {
const jsonValue = await AsyncStorage.getItem('#habit');
let data = JSON.parse(jsonValue) // parse to modify
// push newHabit as well
data.push(newHabit)
setStorageItems(data);
} catch (error) {
console.error(error);
}
in both approaches, stringify the object in the end
await AsyncStorage.setItem('#habit', JSON.stringify(storageItems));

Related

How do I update my list after POST if im using a different endpoint?

I need help with updating my list after POST. I can't see any answers online. Usually what I will just do is push object into array but I think in my case is different.
This function uses 2 api endpoint. The first api will get the list data. The weather api will base from the first api endpoint data, iterate through the list and get the data of the city that matches the name.
async getPreviousWeather() {
let endpoint = "/api/";
let promises = [];
try {
const response1 = await axios.get(endpoint);
this.cities = response1.data;
for (let i = 0; i < this.cities.length; i++) {
const response2 = await axios.get(
`https://api.openweathermap.org/data/2.5/weather?q=${this.cities[i].city_name}&units=metric&appid={API_KEY}`
);
this.infos.push(response2.data);
}
} catch (error) {
console.log(error);
}
},
Now this endpoint post data from the first endpoint. The only problem that I have here is how to update the list on post. I don't know how to push it or i tried calling this.getPreviousWeather(); what happens is it adds the new data but also adds the previous ones.
async onSubmit() {
let endpoint = "/api/";
try {
const response = await axios.post(endpoint, {
city_name: this.city_query,
});
this.city_query = null;
this.getPreviousWeather();
} catch (error) {
console.log(error);
}
},
created() {
this.getPreviousWeather();
},
I created the answer I'm not sure if it is effective but it works.
methods: {
async onSubmit() {
let endpoint1 = `https://api.openweathermap.org/data/2.5/weather?q=${this.city_query}&units=metric&appid={API_KEY}`;
let endpoint2 = "/api/";
try {
const response1 = await axios.get(endpoint1);
const response2 = await axios.post(endpoint2, {
city_name: this.city_query,
});
this.city_query = null;
if (this.error) {
this.error = null;
}
this.infos.push(response1.data);
} catch (error) {
console.log(error);
}
},

How can I update an array field with a batch write operation in firestore?

I've been trying to update an array of maps in my document while creating a new document in a sub-collection but couldn't make it work:
export const addTask = async (data, caseId) => {
let batch = await db.batch();
const caseRef = db.collection("cases").doc(caseId);
const taskRef = caseRef.collection("tasks").doc();
try {
await batch.set(taskRef, data);
await batch.set(caseRef, {
tasks: db.FieldValue.arrayUnion(data),
}, {merge:true});
} catch (error) {
console.log(error);
}
return batch.commit();
};
These are the issues:
use batch.update on the second batch call if the array field already exists on the parent doc and just needs to be updated.
FieldValue.arrayUnion isn't a method on the client instance of firestore. Use firebase.firestore.FieldValue.arrayUnion from the global firebase namespace to update the array. Check the guide on updating arrays
{merge:true} is not required as arrayUnion will add data to the existing set unless it already exists.
export const addTask = async (data, caseId) => {
let batch = await db.batch();
const caseRef = db.collection("cases").doc(caseId);
const taskRef = caseRef.collection("tasks").doc();
try {
await batch.set(taskRef, data);
await batch.update(caseRef, {
tasks: firebase.firestore.FieldValue.arrayUnion(data),
});
}catch (error) {
console.log(error);
}
return batch.commit();
}

Array of strings getting converted to Objects

I'm pushing files to amazon using pre-signed URLs, and modifying the files array with the file name reference inside the newData object. (The files array are inside an array of objects called items)
// Add job
const addJob = async(data, user) => {
const newData = { ...data };
data.items.map((item, itemIndex) => {
if (item.files !== []) {
item.files.map(async(file, fileIndex) => {
const uploadConfig = await axios.get(`/api/s3upload`, {
params: {
name: file.name,
},
});
console.log(uploadConfig.data.key);
newData.items[itemIndex].files[fileIndex] = uploadConfig.data.key;
await axios.put(uploadConfig.data.url, file);
});
}
});
console.log(newData);
try {
const res = await axios.post('/api/jobs', newData);
dispatch({
type: ADD_JOB,
payload: res.data,
});
} catch (error) {
console.log(error);
}
};
The file references comes in the uploadConfig.data.key and are being save into the newData object.
When this function is executed, something peculiar happens:
the console log of newData returns the correct array of references to the files
the files are uploaded just fine
the request made to /api/jobs, which is passing newData, sends an array of objects that contains { path: ... }
console.log(newData):
Post request:
JavaScript does this because forEach and map are not promise-aware. It cannot support async and await. You cannot use await in forEach or map.
for loops are promise-aware, thus replacing the loops with for loops and marking them as await returns the expected behaviour.
source: zellwk article
Corrected (functioning) code:
const addJob = async (data, user) => {
const newData = { ...data };
const { items } = data;
const loop = async () => {
for (let outer in items) {
if (items[outer].files !== []) {
const loop2 = async () => {
for (let inner in items[outer].files) {
const uploadConfig = await axios.get(`/api/s3upload`, {
params: {
name: items[outer].files[inner].name,
},
});
const res = await axios.put(uploadConfig.data.url, items[outer].files[inner])
newData.items[outer].files[inner] = uploadConfig.data.key;
}
};
await loop2();
}
}
};
await loop();
try {
const res = await axios.post('/api/jobs', newData);
dispatch({
type: ADD_JOB,
payload: res.data,
});
} catch (error) {
console.log(error);
}
};

Getting the API by the callback function in React

I have a function, that connects to the API and returns the data:
import {API_KEY, API_URL} from "./constants";
// /**
// * Fetch all tasks
// * #param {function} successCallback - Function that saves incoming data
// */
export const getOperations = async (id, successCallback) => {
try {
const response = await fetch(`${API_URL}/tasks/${id}/operations`, {
headers: {
Authorization: API_KEY,
},
});
const data = await response.json();
if (data.error) {
throw new Error('Error!');
}
successCallback(data.data);
} catch (err) {
console.log(err);
}
};
Then, in one of my react component i call that function to get a data from the specified API:
The props is a required ID.
const [operations, setOperations] = useState([])
console.log(props)
useEffect(() => {
try {
getOperations(data => (props, setOperations(data)))
} catch(e) {
console.log(e)
}
}, [])
The problem is, that my API looks like:
`...api/tasks/function%20(data)%20%7B%20%20%20%20%20%20%20%20return%20props,%20setOperations(data);%20%20%20%`20%20%20%7D/operations`
So i receive 400 error.
Could someone explain me how to get API URL in this situation like:
/api/tasks/{id}/operations
Thanks in advance.
Rather than passing the callback to the result of the function, you could just return the data.
export const getOperations = async (id) => {
try {
const response = await fetch(`${API_URL}/tasks/${id}/operations`, {
headers: {
Authorization: API_KEY,
},
});
const data = await response.json();
if (data.error) {
throw new Error('Error!');
}
return data.data;
} catch (err) {
console.log(err);
}
};
useEffect(() => {
async function apiCall() {
try {
const data = await getOperations(props.id);
setOperations(data)
} catch(err) {
console.log(err)
}
}
apiCall();
}, [props.id])

Async variable appearing empty in conditional logic, yet it is present in console logs

I'm a bit stumped as to how to correct this async issue. I have data vocab that I know I am successfully populating based on data from my database. However, at this time I can't quite figure out how to properly access that async data later in my function. Thoughts?
export const fsFetchLessonVocab = async (
language: string,
id: string,
includeTranslations?: boolean,
translationLanguage?: string
) => {
const lesson = await fsFetchLesson(language, id).then((res) => res);
const lessonContent = await lesson.contentID.get();
const vocab = [];
try {
await lessonContent.data().words.forEach((word) => {
word.get().then((res) => {
vocab.push({ ...res.data(), id: res.id });
});
});
return vocab;
} catch (error) {
console.log('Error retrieving content', error);
}
if (includeTranslations) {
console.log('inbound vocab', vocab); // YES - vocab array has content
if (vocab.length) {
// NO - vocab is an empty array.
const translations = await vocab.forEach((word) => {
console.log('word', word);
});
console.log('translations', translations);
}
}
}
more details
const vocab = [];
try {
await lessonContent.data().words.forEach((word) => {
word.get().then((res) => {
vocab.push({ ...res.data(), id: res.id });
});
});
return vocab;
} catch (error) {
console.log('Error retrieving content', error);
}
^ This block is working as expected. My content document is reached, and I iterate through the response array of words, finding each reference and pushing it into vocab.
if (includeTranslations) {
console.log('inbound vocab', vocab); // Populated array
if (vocab.length) {
// Empty array
const translations = await vocab.forEach((word) => {
console.log('word', word);
});
console.log('translations', translations);
}
}
^ the array vocab is appearing populated in the console.log(), however it's appearing empty within the if() block.
Continued:
I've found that whilte vocab shows and array with content, vocab.length shows 0 in the log.
try maybe this code:
try {
await Promise.all(
lessonContent.data().words.map(async word => {
const res = await word.get();
vocab.push({ ...res.data(), id: res.id });
}),
);
return vocab;
} catch (error) {
console.log('Error retrieving content', error);
}
if res.data() is async as well you need to await for that as well

Categories