Updating a state variable always replaces it instead of merging it. How can I merge it? Function addFruits receives multiple objects like:
{fruitName: banana} {fruitName: apple} {fruitName: watermelon}
How to push this fruitName's into fruits state?
My code returns only the last fruitName. But how to merge all fruitNames like array.push(val);
const [fruits, setFruits] = useState([]); // this is the state
function addFruits(value){
Api.get(`${PATH.GET_SELECTED_FRUITS}/${value}/fruits`).then(res => { // this method gives me multiple object, (like {fruitName: banana} {fruitName: apple} {fruitName: watermelon})
res.map(result => {
setProps(result.fruitName);
})
})
}
You need to combine the existing fruits array with the result from the API:
.then((res) => {
setFruits([
...fruits,
...res.map(fruit => fruit.fruitName)
]);
});
You can merge the response with the state -
function addFruits(value){
Api.get(`${PATH.GET_SELECTED_FRUITS}/${value}/fruits`).then(response => {
totalFruits = [...fruits,
...response.map(fruit => fruit.fruitName)]
setFruits(totalFruits);
})
})
Related
I have array containing objects each object has two props one of it is a observable value
let myArray = [{def: 'name1', value: EventEmitter_}, {def: 'name2', value: EventEmitter_}]
What im trying to do is subscribe to the Observables and and return the root Object where the change occurred
so far I only get the specific value
myArray.forEach(e => {
e.value.subscribe(e => console.log(e))
})
I tried using merge
merge(myArray).subscribe((v)=> {
console.log(v)
})
but that does not work if the Observable is nested
You could pipe the value stream.
myArray.forEach(e => {
e.value.pipe(
map(v => [e.def, v]),
).subscribe(([d, v]) => console.log(`Root ${d} returned %{v}`));
})
obs = merge(
...this.array.map((x: any) =>
x.value.pipe(map((res) => ({ def: x.def, response: res })))
)
);
You merge the "observables" transformed with a property "def", and a property "response". So you only need one subscripcion
See stackblitz
I have a collection, where each single document have an array of references.
The structure in Firestore is like this:
collection1
doc1
doc2
doc3
name: string
...
refs: Array
collection2/x786123nd...
collection2/z1237683s...
...
I want to convert that into:
[
{<doc1 as object>},
{<doc2 as object},
{ name: "Document 3", refsResult:
[
{<object here mapped from reference>},
{<object here mapped from reference>},
]
}
]
Long story short, from firebase I need to get an output of a list of object where each object has a list of objects instead of references.
THE FOLLOWING SNIPPET IS NOT WORKING
I am trying to do it at service level in Angular using RxJS to transform the output, something like this:
return this.afs.collection('collection1')
.valueChanges()
.pipe(
switchMap(objects => from(objects).pipe(
switchMap(obj => from(obj.refs).pipe(
switchMap(ref => ref.get()),
map(r => ({ ...obj, refsResult: r.data() })),
tap(obj => console.log('OBJ', obj)),
)),
)),
tap(objects => console.log(objects))
);
But it seems that I only receive one object instead of a list of objects with 2. Also, it seems that the refsResult is also a single object instead of an array. I am sure I am using the switchMaps wrong, probably they are cancelling the others results or similar.
I would like to keep the solution within the chain of rxjs operators.
EDIT 1
I got it working in a hacky way, I am just sharing it here in order to give more context, and see if we can find a way to do it with RxJS.
return this.afs.collection('collection1')
.valueChanges()
.pipe(
tap(objects => {
objects.forEach(object => {
object.refsResult = [];
object.refs.forEach(ref => {
ref.get().then(d => object.refsResult.push(d.data()));
})
})
}),
tap(programs => console.log(programs))
);
The code above works but not very well, since first return the collection and then, mutates the objects to inject the items within the array. Hopefully it helps.
Help is very appreciated! :)
You can use combineLatest to create a single observable that emits an array of data.
In your case, we can use it twice in a nested way:
to handle each object in collection
to handle each ref in refs array for each object
The result is a single observable that emits the full collection of objects, each with their full collection of ref result data. You then only need a single
switchMap to handle subscribing to this single observable:
return this.afs.collection('collection1').valueChanges().pipe(
switchMap(objects => combineLatest(
objects.map(obj => combineLatest(
obj.refs.map(ref => from(ref.get()).pipe(map(r => r.data())))
).pipe(
map(refsResult => ({ ...obj, refsResult }))
)
))
))
);
For readability, I'd probably create a separate function:
function appendRefData(obj) {
return combineLatest(
obj.refs.map(ref => ref.get().pipe(map(r => r.data())))
).pipe(
map(refsResult => ({ ...obj, refsResult }))
);
}
return this.afs.collection('collection1').valueChanges().pipe(
switchMap(objects => combineLatest(objects.map(appendRefData)))
);
How can I access array data inside my json object data?
const [data, setData] = useState([])
const getData = () => {
axiosInstance
.get(url + slug)
.then(result => setData(result.data))
}
useEffect(() => {
getData()
}, [])
Here are something I've tried:
console.log(data['symbol'])
console.log(data[0]['symbol'])
console.log(data[0].symbol)
Here is the data being used in my state:
To keep it simple, lets say I want to the access first array, and console log all values for symbol. How would I go about doing that?
Your object data looks like an array of arrays of objects.
This should work:
data[0][0].symbol
To log all the symbols of the first array:
console.log(data[0].map(item => item.symbol))
To access the first array you can use this data[0] to map through all the objects inside the first array and log them you do the following
data[0].map(item => console.log(item.symbol))
I get the warning in the title when compiling. I understand that it is about not handling some cases of if, but how can I filter before mapping in the correct way?
componentDidMount() {
this.props.UserReducer.user.employeeInfoList.map(role => {
if (role.employeeType) this.rolesOfUser.push(role.employeeType);
if (role.xdockId) this.xdockIdsOfUser.push(role.xdockId);
});
}
It is because you are misusing map which is used for mapping/transforming one array to another. Having a call to map without a return value indicates a problem, as you shouldn't be using it to just iterate over an array performing some action.
It looks like what you really wanted was a forEach call.
To filter an array use Array#filter. Also you can use Array#forEach for your case
componentDidMount() {
this.props.UserReducer.user.employeeInfoList.forEach(role => {
if (role.employeeType) this.rolesOfUser.push(role.employeeType);
if (role.xdockId) this.xdockIdsOfUser.push(role.xdockId);
});
}
Or
componentDidMount() {
const rolesOfUser = this.props.UserReducer.user.employeeInfoList.filter(role => {
return role.employeeType;
})
const xdockIdsOfUser = this.props.UserReducer.user.employeeInfoList.filter(role => {
return role.xdockId;
})
// Do smth with both arrays
}
I have an object with two arrays as properties:
I want to populate the arrays by running promises in series.
I fetch the result of the promises, and map a function to decorate all the items in my arrays.
While one array get populated and persist, the other get populated only while in the map function, but at the end the array is returned still empty.
Can you help to understand why?
I check the Promise is actually returned, and indeed in one case it works, not in the other.
this is my pseudo-code:
function formatMyObject( arrayOfIds ) {
// initialize the objet
var myObj = {
decorators = [],
nodes = []
...
}
// I map the Promise reconciliate() and push the results in the array:
return reconciliateNode(arrayOfIds)
.then( data => {
data.map( node => {
// I fetch results, and myObj.nodes
myObj.nodes.push( { ... })
})
})
return myObj
})
.then( myObj => {
// myObj.nodes is now a NON empty array
// I want to the same with myObj.decorators:
var data = myObj.nodes
// I think I am doing just as above:
data.map( node =>
decorateNode(node.source)
.then( decoration => {
decoration = decoration[node.source]
myObj['decorators'].push( {
...
} )
// I check: the array is NOT empty and getting populated:
console.log('myObj.decorators', myObj)
debugger
})
)
// instead now, just after the map() function, myObj.decorators is EMPTY!
console.log('myObj.decorators', myObj);
debugger
return myObj
)
... // other stuff
}
As in the second case the map callback returns a promise, that case is quite different from the first case.
In the second case, you would need to await all those promises, for which you can use Promise.all.
The code for the second part could look like this:
.then( myObj => {
return Promise.all(myObj.nodes.map(node => decorateNode(node.source)));
}).then(decorations => {
myObj.decorators = decorations.map(decoration => {
decoration = decoration[node.source];
return ({
...
});
})
console.log('myObj.decorators', myObj);
return myObj;
})