How to asynchronously manipulate a deeply nested object by referennce - javascript

I am working on a gridsome project, where I am manipulating a schema and rendering pages based on that. So, I am passing a reference of schema to a function, which function will manipulate a deeply nested object like,
{
URL: "some.url"
paramas: {"some params}
}
to
{
URL: "some.url"
params: {"some params}
props:{
data:[] || {}
}
}
this manipulation will happen based on the URL and param, so I need to use forEach loop but unfortunately, forEach loop is not asynchronous.
Here I am out of ideas what I need to do if I want to manipulate all those objects (more than 100) from other module and it has to be asynchronous.
a demo schema:
{
"name": "home",
"path": "/",
"header": "top_header",
"footer": "bottom_footer",
"rows": [
{
"row_class": "bg-white",
"columns_class": "cols-66-33 cols-separated cols--height-equal",
"column_1": [
{
"name": "Coral/HomeNews",
"props": {
"tag": "main"
}
}
],
}
]
}
edit:
So I am calling a function from other module like
await dataExtender(_schema);
which contains a function(currently I am developing it, and not complete) which is
const dataExtender = async _schema => {
// Define forEach function
console.log(chalk.green("Data Extending starts from module level"));
await Promise.all(
_schema.pages.forEach(async page => {
page.rows.forEach(row => {
row.column_1.forEach(async column => {
if (column.url !== undefined) {
await axios
.post(column.url, column.param || {})
.then(data => {
console.log(chalk.green("Extending"));
if (Array.isArray(data.data)) {
column.props.data = [...data.data];
} else {
column.props.data = { ...data.data };
}
})
.catch(err => {
console.log(err);
});
}
});
if (row.column_2 !== undefined) {
row.column_2.forEach(async column => {
if (column.url !== undefined) {
await axios
.post(column.url, column.param || {})
.then(data => {
column.props.data = data.data;
})
.catch(err => {
console.log(err);
});
}
});
}
});
}),
);
console.log(chalk.green("Data Extending finishes from module level"));
return _schema;
};

While forEach itself is not async-aware, that doesn't stop you from being able to push the asynchronous modification promises onto a list and awaiting for all of them at the end.
import axios from "axios";
async function processColumn(column) {
const data = await axios.post(column.url, column.param || {});
console.log(chalk.green("Extending"));
if (Array.isArray(data.data)) {
column.props.data = [...data.data];
} else {
column.props.data = { ...data.data };
}
}
async function processColumn2(column) {
const data = await axios.post(column.url, column.param || {});
column.props.data = data.data;
}
const dataExtender = async (_schema) => {
console.log(chalk.green("Data Extending starts from module level"));
const promises = [];
_schema.pages.forEach((page) => {
page.rows.forEach((row) => {
row.column_1.forEach((column) => {
if (column.url !== undefined) {
promises.push(processColumn(column));
}
});
if (row.column_2 !== undefined) {
row.column_2.forEach((column) => {
if (column.url !== undefined) {
promises.push(processColumn2(column));
}
});
}
});
});
await Promise.all(promises);
console.log(chalk.green("Data Extending finishes from module level"));
return _schema;
};

Related

Check two async boolean variables and call a method if both are satisfied

How could I improve this method of rendering only when both variables are met as true, to allow the renderFilters() method to be called:
These two variables are filled asynchronously through 2 API methods:
//getManager()
this.isLoadingManager = true;
//getPdiPOrganization()
this.isLoadingPdiOrganization = true;
promiseRender() {
let interval = setInterval(() => {
if (this.isLoadingManager && this.isLoadingPdiOrganization) {
clearInterval(interval);
this.renderFilters();
} else {
setTimeout(() => {
clearInterval(interval);
this.renderFilters();
}, 5000)
}
}, 500);
}
The problem is that it's very slow... it's calling long after the APIs are called...
Maybe some feature of angular itself, if anyone has a better solution...
const observable = forkJoin({
loading1:this.isLoadingManager,
loading2:this.isLoadingPdiOrganization
});
observable.subscribe({
next: (results) => {
const obs1Val = results[0];
const obs2Val = results[1];
if (obs1Val && obs2Val) {
this.renderFilters();
}
}
})
Or:
const myObservable = Observable.of(this.isLoadingManager && this.isLoadingPdiOrganization);
const myObserver = {
next: (result: Boolean) => this.renderFilters(),
};
myObserver.next(true);
myObservable.subscribe(myObserver);
Adding the methods:
getManager() {
if (this.fromAdminPage && localStorage.getItem('_receivers_pdi')) {
this.meetingService.getIsManager()
.subscribe(res => {
this.showPdiToastNotification = res;
this.isLoadingManager = true;
});
}
}
getPdiPOrganization() {
const url = this.publicEndpoint ? 'current/organization/pdi/configuration' : 'api/current/organization/pdi/configuration';
const requestOptions = {
params: new CustomHttpParams({ isPublicTokenUrl: this.publicEndpoint })
};
this.http.get<any>(url, requestOptions).subscribe(resp => {
this.isLoadingPdiOrganization = true;
this.pdiOrgConfig = resp || {};
this.updatePdiReferenceType(this.pdiOrgConfig);
});
}
You can use forkjoin to subscribe to two observables at the same time. I would stick with using RxJs operators for cases like these. You can read more about forkJoin here.
forkJoin([obs1, obs2]).subscribe({
next: (results) => {
const obs1Val = results[0];
const obs2Val = results[1];
if (obs1Val && obs2Val) {
this.renderFilters();
}
}
});

Problem get unstructured object - Angular

i am student
Why does it return undefined?
It is an object I need to perform destructuring to obtain only two values.
I can get complete array, but trying to unstructured it returns undefined.
service
getLastCashMov(): Observable<CashMov> {
const url = `${this.API_URL}/last_cashmov`
return this.http.get<CashMov>(url)
}
component
getLastCashMov() {
this.cashMovsService.getLastCashMov()
.subscribe(({balanceARS, balanceUSD}) =>
{ this.lastCashMovs = {balanceARS, balanceUSD};
console.log(balanceARS, balanceUSD)
}, error => {
console.log(error)
})
}
return
when using
getLastCashMov() {
this.cashMovsService.getLastCashMov()
.subscribe((res) =>
{ this.lastCashMovs = res;
console.log(this.lastCashMovs)
}, error => {
console.log(error)
})
}
return
SOLUTION
getLastCashMov() {
this.cashMovsService.getLastCashMov()
.subscribe(([{balanceARS, balanceUSD}]) =>
{ this.lastCashMovs = {balanceARS, balanceUSD};
console.log(this.lastCashMovs)
}, error => {
console.log(error)
})
}
SERVICE
getLastCashMov(): Observable<CashMov[]> {
const url = `${this.API_URL}/last_cashmov`
return this.http.get<CashMov[]>(url)
}
We can't Unstructure array of object for some keys. we need to update and filter keys through map or filter
Here is the code :
getLastCashMov() {
this.cashMovsService.getLastCashMov()
.subscribe((responseArray) => {
let filteredArray = responseArray.map(item => return { balanceARS : item.balanceARS, balanceUSD: item.balanceUSD})
// filteredArray = [{ balanceARS : "...", balanceUSD: "..."}]
this.lastCashMovs = filteredArray[0];
console.log(balanceARS, balanceUSD)
}, error => {
console.log(error)
})
}

How put Firebase object to state in react?

I'm learning react and I'm having difficulty putting data from Firebase into the application state. I use Rebase but I am open to any solution!
still have an error similar to this one :
Thank you for your help !
Here is the code :
class App extends Component {
state = {
user:'',
vampires: {}
}
componentDidMount() {
if(this.state.user === ''){
firebase.auth().onAuthStateChanged(user => {
if(user){
this.handleUserLogin({ user })
this.setVampires({ user })
} else {
console.log('error')
}
})
}
}
setVampires = async (authData) => {
console.log(this.state.user)
await base.fetch(`/${authData.user.uid}/vampires`, { context: this })
.then(data => {
console.log(data)
let vampires = this.state.vampires;
vampires = {..._.cloneDeep(data)};
this.setState({vampires: {vampires}})
})
}
handleUserLogin = async authData => {
this.setState({user: authData.user.uid})
}
Your Firebase data is returned as an Object, with properties VampM5-..... React expects that you pass any repeated data as an array, not as an object.
So you'll need to convert the data from Firebase to an array, for example with:
await base.fetch(`/${authData.user.uid}/vampires`, { context: this })
.then(data => {
vampires = [];
data.forEach((child) => {
vampires.push({ ...child.val(), ".key": child.key });
})
this.setState({ vampires: vampires })
})

Can't set state in react

So, I'm simply trying to set state in my react app. Simply get data from Axios, and then set state. But no matter what I do, the state will not set. I've tried putting it in a callback since it's async and putting it my component did mount and component did update alas nothing. any pointers?
class App extends Component {
componentDidUpdate() {}
constructor(props) {
super(props);
this.state = {
Catogories: [
"Business",
"Entertainment",
"General",
"Health",
"Science",
"Sports",
"Technology"
],
CatPics: [],
TopStories: [],
Selection: [],
Sources: [],
selected: false
};
}
GeneratePic = () => {
this.state.Catogories.forEach(Catogory => {
axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
var object = { Catogory: res.photos[0].src.large2x };
this.state.CatPics.push(object);
});
});
};
dump = x => {
this.setState({ TopStories: x }, console.log(this.state.TopStories));
};
TopStories = () => {
console.log("working");
axios
.get(
"https://newsapi.org/v2/top-headlines?country=us&apiKey=91bec895cf8d45eaa46124fb19f6ad81"
)
.then(res => {
console.log(res);
const data = res.data.articles;
console.log(data);
this.dump(data);
});
};
You are doing two things wrong.
Don't mutate the state
Don't do async actions inside loop and then use same loop variable inside async callback because at that point in time, loop variable will have some other value and not the respective iteration category.
GeneratePic = async () => {
const promises = this.state.Catogories.map(Catogory => {
return axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
return res.photos[0].src.large2x;
});
});
let photos = await Promise.all(promises);
photos = this.state.Catogories.map((cat, index) => ({ [cat]: photos[index] }));
this.setState({ CatPics: photos });
};
getPics = cat => {
return axios
.get(
"https://api.pexels.com/v1/search?query=" +
cat +
"&per_page=15&page=1",
{
Authorization:
"563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
return { [cat]: res.photos[0].src.large2x };
});
}
GeneratePic = async () => {
const promises = this.state.Catogories.map(Catogory => {
this.getPics(Catogory);
});
let photos = await Promise.all(promises);
this.setState({ CatPics: photos });
};
This should work.
Dont Mutate the state.
GeneratePic = () => {
this.state.Catogories.forEach(async Catogory => {
await axios
.get(
"https://api.pexels.com/v1/search?query=" +
Catogory +
"&per_page=15&page=1", {
Authorization: "563492ad6f91700001000001d33b5d31a9a145b78ee67e35c8e6c321"
}
)
.then(res => {
var object = { Catogory: res.data.photos[0].src.large2x };
const cPics = [...this.state.CatPics];
cPics.push(object);
this.setState({
CatPics: cPics
})
});
});
};

Objects in array are empty when return statement is included in function

I have a function that I want to return the array of objects parsedContacts. With the return statement there, the console.log above it prints an array of empty objects. When I remove the return statement, each object has three properties as expected.
How can I return the parsedContacts and include the properties?
/* eslint-disable no-console */
/* eslint-disable no-unused-vars */
import { PermissionsAndroid } from 'react-native';
import Contacts from 'react-native-contacts';
export const getAndProcessPhoneContacts = async () => {
PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
{
'title': 'Contacts',
'message': 'Xxxxxxxx would like to view your contacts.'
}
)
const contacts = await getContacts();
const parsedContacts = parseContacts(contacts);
sortContacts(parsedContacts);
console.log(parsedContacts);
return parsedContacts; // this line needs to be removed for parsedContacts to have properties in the objects inside it.
}
const getContacts = () => {
return new Promise((resolve, reject) => {
Contacts.getAll((error, contacts) => {
contacts ? resolve(contacts) : reject(error)
})
})
}
const parseContacts = contacts => {
return contacts.map(contact => {
let parsedContact = {}
Object.keys(contact).forEach(key => {
switch (key) {
case 'givenName':
parsedContact.firstName = contact[key]
break
case 'familyName':
parsedContact.surname = contact[key]
break
case 'phoneNumbers':
parsedContact.phoneNumber = contact[key].length ? contact[key][0].number : ''
}
})
return parsedContact
})
}
const sortContacts = contacts => {
contacts.sort((a, b) => {
let contactA = a.firstName;
let contactB = b.firstName;
return (contactA < contactB) ? -1 : (contactA > contactB) ? 1 : 0;
});
}
Update
As requested in the comments below, here is the calling function of getAndProcessPhoneContacts. I know that this is ugly and needs refactoring, any advice on this gratefully accepted too!
async componentDidMount() {
ConnectyCube.init(...config)
try {
const accessToken = await getFirebaseToken();
if (accessToken) {
await authorizeConnectyCube(accessToken);
if (this.props.user.parsedContacts) {
const registeredUsers = await retrieveRegisteredUsers();
this.props.updateRegisteredContacts(registeredUsers);
Actions.Dashboard();
} else {
const parsedContacts = await getParsedContactsFromStorage();
if (parsedContacts) {
this.props.updateParsedContacts(parsedContacts);
Actions.Dashboard();
} else {
const parsedContacts = await getAndProcessPhoneContacts();
console.log(parsedContacts); // prints an array of empty objects
await writeParsedContactsToStorage(parsedContacts);
this.props.updateParsedContacts(parsedContacts);
const registeredUsers = await retrieveRegisteredUsers();
this.props.updateRegisteredContacts(registeredUsers);
Actions.Dashboard();
}
}
} else {
Actions.PhoneNumberInput();
}
} catch (error) {
Alert.alert(error);
}
}
Update 2
I have an inelegant solution by using a callback:
const cb = (ct) => {
console.log(ct); // Objects now have properties
}
const parsedContacts = await getAndProcessPhoneContacts(cb);
await writeParsedContactsToStorage(parsedContacts);
this.props.updateParsedContacts(parsedContacts);
const registeredUsers = await retrieveRegisteredUsers();
this.props.updateRegisteredContacts(registeredUsers);
Actions.Dashboard();
}
}
} else {
Actions.PhoneNumberInput();
}
} catch (error) {
Alert.alert(error);
}
}
And the called function:
export const getAndProcessPhoneContacts = async (cb) => {
PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
{
'title': 'Contacts',
'message': 'Xxxxxxx would like to view your contacts.'
}
)
const contacts = await getContacts();
const parsedContacts = parseContacts(contacts);
sortContacts(parsedContacts);
console.log(parsedContacts);
cb(parsedContacts)
}

Categories