dependabot[bot] on npm_and_yarn
jayphelps on master
chore(deps): bump trim-off-newl… (compare)
dependabot[bot] on npm_and_yarn
chore(deps): bump trim-off-newl… (compare)
jayphelps on master
docs(examples): update to use `… (compare)
dependabot[bot] on npm_and_yarn
dependabot[bot] on npm_and_yarn
dependabot[bot] on npm_and_yarn
function fetchCharactersEpic(action$) {
return action$.pipe(
ofType(FETCH_CHARACTERS),
mergeMap(() =>
ajax.getJSON(api, { Authorization: `Bearer ${API_TOKEN}` }).pipe(
flatMap((data) => Array.from(data.docs.text())),
map((characters) =>
characters.map((character) => ({
id: character._id,
name: character.name,
wikiUrl: character.wikiUrl,
spouse: character.spouse,
}))
),
map((characters) => fetchCharactersSuccess(characters)),
catchError((error) =>
Observable.of(fetchCharactersFailure(error.message))
)
)
)
);
}
That's a good question. This is actually a React problem.
Server-side React doesn't run useEffect
and will not work async.
The only way you can get this to work as you expect is to subscribe to store
and wait for the values you want to exist. After that, render React server-side.
I know it's weird, but React doesn't have a way to run and run again when certain changes are made server-side. The way it works, it renders HTML and that's it. So you have to do all the work before React runs, then feed React all the data necessary to get the correct server-side output you're wanting.
Actions must be plain objects. Use custom middleware for async actions.
const fetchMenu = (action$, _state$) => {
return action$.pipe(
ofType(Types.FETCH_MENU_REQUESTED),
flatMap((async ({ resID }) => {
const firestore = firebase.firestore();
const menuRef = firestore.collection('menus').where('resID', '==', resID);
return collectionData(menuRef, 'id')
.pipe(
map(val => {
console.log('vals');
console.log(val);
return Creators.fetchMenuSuccess(val);
})
)
}
)),
);
};
Hi everyone, I have a question about combineLatest in Epic, after my app has been initiated, it will dispatch initAppCompleted only once, after that every time I jump to the focus page, it will dispatch fetchFocusPage action and request API.
Based on the info in https://stackoverflow.com/questions/42426908/how-to-delay-one-epic-until-another-has-emitted-a-value, using combineLatest must be alright.
But what happened is: after clicking the focus page, only the first time it outputs 'focus page epic entering'. Only If I manually dispatch initAppCompleted action after clicking the page, otherwise there is no request happening. I really don't know why.
here is my code(using Rxjs 6.5.4 & redux-observable 1.2.0):
export const focusPageFetchEpic: Epic<AnyAction, AnyAction, any> = (
action$: ActionsObservable<Action>,
state$: StateObservable<any>
) =>
combineLatest([
action$.pipe(filter(isActionOf(initAppCompleted)), tap(() => console.log('init action comming')), take(1)),
action$.pipe(filter(isActionOf(fetchFocusPage)), tap(() => console.log('fetch action comming')))
]).pipe(
tap(() => console.log('focus page epic entering'))
)
@fisherspy It shouldn't matter about the payload. If both observables fire once, then it outputs values in your pipe
. That's how combineLatest
works. It waits for observables to output something once. After that happens, then you start receiving values in the combineLatest().pipe
.
If you want something to emit when either observable emits, use merge
instead of combineLatest
.
import { ActionsObservable } from 'redux-observable'
// ...
export const mockAjaxResponse = (
mockedResponse,
) => ({
ajax: () => (
ActionsObservable
.of({
response: mockedResponse,
})
),
})
// ...
fetchUserEpic(
// action$
ActionsObservable
.of(
fetchUser({ // this is a Redux action creator
username: 'test@test.com',
password: 'test',
})
),
// state$
new BehaviorSubject({
user: null,
}),
// Dependency injection
mockAjaxResponse(),
)
@xavier7179 I'd need a bit of code to work with. Can't visualize what you mean.
You can always do something like dispatching an action from those epics making API calls and then have a single epic do something like:
combineLatest(
action$.pipe(ofType('FIRST_API_RESPONSE')),
action$.pipe(ofType('SECOND_API_RESPONSE')),
)
@Sawtaytoes sure!
The schema I'm depicting in my mind is something like (avoiding parallelism and keeping the order):
So far, based on your previous suggestion, I did something like this (which it is not fully including all scenarios but epic error):
export function fetchEpics(action$) {
return action$.pipe(
ofType(FETCH_EPIC1),
take(1),
mergeMap(
(original_action) =>
concat(
of(fetchEPIC1(original_action.payload)),
action$.ofType(FETCH_EPIC2).pipe(
take(1),
map(() => fetchEPIC2(original_action.payload)
)
),
),
)
);
}
But as soon as I try a third one, I got stuck.
@xavier7179 Is there any reason you can't move this code to another epic?
action$.ofType(FETCH_EPIC2).pipe(
take(1),
map(() => fetchEPIC2(original_action.payload))
I know you need original_action.payload
. When your action completes, can you add originalActionPayload: original_action.payload
to the action?
@xavier7179 Is there any reason you can't move this code to another epic?
Sorry, I'm not sure I got the question...
I know you need
original_action.payload
. When your action completes, can you addoriginalActionPayload: original_action.payload
to the action?
I can certainly add it. Is it the issue I'm facing figuring out how to chain several epics in sequence, avoiding parallelism?
I'm not sure if nesting is the right approach. what about putting all the epics at the top level? Start the second epic with the success action of the first. Have a separate epic to further process the errors or combine the logic into a custom operator
But this way I cannot call epics alone... am I right?
`const createStoreWithMiddleware = applyMiddleware('
`promiseMiddleware,`
`ReduxThunk`
`)(createStore);`
`ReactDOM.render(`
`<Provider`
` store={createStoreWithMiddleware(`
` Reducer,`
` window.__REDUX_DEVTOOLS_EXTENSION__ &&`
` window.__REDUX_DEVTOOLS_EXTENSION__()`
` )}`
` >`
` <BrowserRouter>`
` <App />`
` </BrowserRouter>`
`</Provider>,`
@evertbouw @Sawtaytoes I actualy solved 90% of the issue with the following two epics:
export function fetchAllEpics(action$) {
return action$.pipe(
ofType(FETCH_ALL_EPICS),
take(1),
mergeMap((original_action) =>
concat(
of(fetchFirstEpicAction(original_action.payload)),
action$.ofType(EPIC1_FETCHED).pipe(
take(1),
mergeMap(() =>
concat(
of(fetchSecondEpicAction(original_action.payload)),
action$.ofType(EPIC2_FETCHED).pipe(
take(1),
map(() => fetchThirdEpicAction(original_action.payload))
)
),
)
)
)
)
);
}
export function collectEpicResults(action$) {
return action$.pipe(
ofType(FETCH_ALL_EPICS),
mergeMap((action) =>
combineLatest([
action$.ofType(EPCI1_FETCHED),
action$.ofType(EPIC2_FETCHED),
action$.ofType(EPIC3_FETCHED)
]).pipe(
map(([e1, e2, e3]) =>
fetchAllEpicsFullfilled({ e1: e1.payload, e2: e2.payload, e3: e3.payload })
),
catchError(([error_e1, error_e2, error_e3]) => of(fetchAllEpicsFailed([error_e1, error_e2, error_e3])))
)
)
);
}
where the composition can be done by replacing the map
with a mergeMap
+ concat
block. Everything works fine in the serialization, while the second epic is perfect when everything works without errors. When in the chain some epic fails (notice that each EPIC has its own piping of map and catchError) then, the first epic is stuck. I figure out that maybe the issue is not having two epics but putting all in one epic but... I did not work out how, in order to keep the correct path still fully working.
@andrevmatos That might be something in your code. I've run really complex Redux-Observable applications and performance tests. It's more-likely something where your pipelines are spawning more observables and not getting rid of past ones. Or that the pipelines are computationally intensive.
Is it possible to post any of that code?
from
with scheduled
+ asapScheduler
on some epics which could dispatch intensive side-effects actions did seem to have fixed or worked around the issue, at least preventing other epics from starving. raiden-network/light-client#2281
@andrevmatos Awesome! Glad you were able to fix it! And yes, you are right, using concatMap
is running things sequentially rather than async. Same issue people run into using async-await
.
Redux-Observable has its own scheduler to allow things to speed up, but I'm assuming you needed something even more than that in your scenario. Personally, I'm curious.
Similar to you, I also wrote my own controller for my LIFX lights all in Redux-Observable :).
@JesusTheHun I don't pass callbacks like that anywhere in Redux-Observable , but sometimes I will pass classes with methods if necessary like a flicClient
or eventEmitter
depending.
While it's possible to pass React callbacks to Redux-Observable, that's really strange and wouldn't work if you put your Redux store in a Web Worker or a child process.
The way of doing it is creating a generic epic or epics that work with any form. Add some form of id
and now you can identify form to close regardless of which epic you're in.
Your Modal
component, or whatever you're using to display the form would get hidden because it's listening to the open state of that particular ID.
Does this make sense? If you have code, paste some in, we can help you come up with a plan.
Hi @Sawtaytoes it does make sense, we actually have though about that option and then thought it was a bit overkill. Your worker argument is very good.
The way of doing it is creating a generic epic or epics that work with any form. Add some form of id and now you can identify form to close regardless of which epic you're in.
Why/How a generic epic ? In your business epic you dispatch your success action and in your generic epic you catch all those success/failure and your react to them ?