Reputation: 85
As per the title, I've been trying to figure out a way to make multiple HTTP calls from an action payload and basically merging all results into a single array and then map the result to another action as a payload.
Here is my existing code, it works but it seems it seem not merging both responses and only cares about last response from the request's response, perhaps I'm using the wrong operator, I'm just picking up RxJS and trying different things to learn, hopefully somebody is able to explain where I'm going wrong.
Any help or direction is much appreciated.
{
type: 'FETCH_DATA',
payload: {
key: 'standings',
value: [
'http://url-1',
'http://url-2'
],
})
}
// The epic is listening to the above action
const fetchDataEpic = action$ => action$.pipe(
ofType('FETCH_DATA'),
mergeMap(action => {
const { key, value } = action.payload;
if (Array.isArray(value) && value.length) {
const makeRequest = value.map((x, i) => ajax.getJSON(value[i]))
return (
forkJoin(
makeRequest,
response => {
return ({
type: 'FETCH_DATA_SUCCEEDED',
payload: {
key,
value: response,
},
})
}
)
)
}
})
);
NOTE: Both request's response is in same JSON format with same properties, but different data.
Thanks in advance
Upvotes: 2
Views: 7178
Reputation: 11934
I don't think it only cares about the last response, but the first one.
That's because if you want to customize the response from forkJoin
, the second(or last, in case the first one isn't an array) argument(which is a function) accepts multiple arguments, which are the responses from the supplied requests.
Thus, this is the change I'd perform in order to get the whole array of responses.
/* ... */
forkJoin(
makeRequest,
(...response) => {
return ({
type: 'FETCH_DATA_SUCCEEDED',
payload: {
key,
value: response,
},
})
}
)
/*
This can be achieved by using the tap operator, which is commonly used when you have to deal with side effects.
(
mergeMap(action => { ... }),
tap(action => sendToOtherAction(action))
)
Upvotes: 0
Reputation: 81
it looks like you're using rxjs' ajax function, which should already give you observables. In that case, forkJoin should give you both values as an array once the all the observable have emitted values. And then you can added to your action payload with the map function.
const requestArrays = [1, 2].map(i => {
return rxjs.ajax.ajax.getJSON(`https://jsonplaceholder.typicode.com/posts/${i}`);
});
rxjs.forkJoin(requestArrays)
.pipe(
rxjs.operators.tap(resp => {
console.log('Part 1: Response so far: ');
console.log(resp);
}),
rxjs.operators.map(value => {
return {
type: 'FETCH_DATA_SUCCEEDED',
payload: {
value
}
};
}),
rxjs.operators.tap(resp => {
console.log('Part 2: Result as a action payload: ');
console.log(resp);
}),
)
.subscribe();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
EDIT: Here is roughly how you would implement it.
const action = {
type: 'FETCH_DATA',
payload: {
value: [
'1',
'2'
],
}
};
rxjs.of(action)
.pipe(
rxjs.operators.mergeMap(action => {
const requestArrays = action.payload.value.map(i => {
return rxjs.ajax.ajax.getJSON(`https://jsonplaceholder.typicode.com/posts/${i}`);
});
return rxjs.forkJoin(requestArrays)
.pipe(
rxjs.operators.map(value => {
return {type: 'FETCH_DATA_SUCCEEDED', payload: {value}};
})
)
})
)
.subscribe(console.log);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
Upvotes: 3