Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Oct 20 13:19
    oste commented #539
  • Oct 20 13:13
    oste commented #539
  • Oct 20 12:43
    oste commented #539
  • Oct 20 09:34
    evertbouw commented #539
  • Oct 20 09:18
    evertbouw commented #539
  • Oct 20 05:06
    oste commented #539
  • Oct 20 05:00
    oste commented #539
  • Oct 06 06:42
    jun-sheaf synchronize #733
  • Sep 28 07:56
    evertbouw locked #736
  • Sep 27 23:23
    jayphelps commented #736
  • Sep 27 20:11
    pawelotto commented #736
  • Sep 27 17:35
    jayphelps commented #736
  • Sep 27 17:12
    pawelotto commented #736
  • Sep 27 00:21
    Artizmo commented #736
  • Sep 26 14:53
    jayphelps commented #736
  • Sep 26 13:59
    jayphelps closed #736
  • Sep 26 13:59
    jayphelps commented #736
  • Sep 26 13:46
    pawelotto opened #736
  • Sep 23 15:33
    jayphelps commented #735
  • Sep 23 08:16
    evertbouw opened #735
nzsoori
@nzsoori
import { mergeMap, map } from "rxjs/operators";
import { ofType } from "redux-observable";
import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";

import {
  FETCH_BOOKS,
  fetchBooksFailure,
  fetchBooksSuccess,
} from "../actions/index";

let api = "https://the-one-api.herokuapp.com/v1/book";

function fetchBooksEpic(action$) {

  try {
    return action$.pipe(
      ofType(FETCH_BOOKS), 
      mergeMap(() => {

        return ajax.get(api).pipe(
          map((data) => Array.from(data.docs))         
        );       
      }),
      map((books) => fetchBooksSuccess(books)) 
    );
  } catch (error) {
    Observable.of(fetchBooksFailure(error.message));
  }
}

export default fetchBooksEpic;
import { API_TOKEN, api } from "../constants/demoConstants";

import { switchMap, mergeMap, map, flatMap } from "rxjs/operators";
import { ofType } from "redux-observable";
import { ajax } from "rxjs/ajax";

import { Observable } from "rxjs";
import {
  FETCH_CHARACTERS,
  fetchCharactersSuccess,
  fetchCharactersFailure,
} from "../actions/index";


let xhr = new XMLHttpRequest();
xhr.open("GET", api, true);
xhr.setRequestHeader("Authorization", "Bearer " + API_TOKEN);
xhr.send();

function fetchCharactersEpic(action$) {

  try {
    return action$.pipe(
      ofType(FETCH_CHARACTERS), 
      mergeMap(() => {      

        return ajax.getJSON(api).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)) 
    );
  } catch (error) {
    Observable.of(fetchCharactersFailure(error.message));
  }
}

export default fetchCharactersEpic;
nzsoori
@nzsoori
import { combineEpics } from "redux-observable";

import fetchWhiskiesEpic from "./fetchWhiskiesEpic";
import fetchBooksEpic from "./fetchBooksEpic";
import fetchCharactersEpic from "./fetchCharactersEpic";

export default combineEpics(
  fetchBooksEpic,
  fetchWhiskiesEpic,
  fetchCharactersEpic
);
import {
  FETCH_CHARACTERS,
  FETCH_CHARACTERS_FAILURE,
  FETCH_CHARACTERS_SUCCESS,
} from "../actions/index";

const initialState = {
  isLoaded: false,
  characters: [],
  error: null,
};

const charactersReducer = (state = initialState, action) => {
  switch (action.type) {
    case FETCH_CHARACTERS:
      return {
        ...state,
        // whenever we want to fetch the CHARACTERS, set isLoading to true to show a spinner
        isLoading: true,
        error: null,
      };

    case FETCH_CHARACTERS_SUCCESS:
      return {
        characters: [...action.payload],
        // whenever the fetching finishes, we stop showing the spinner and then show the data
        isLoading: false,
        error: null,
      };

    case FETCH_CHARACTERS_FAILURE:
      return {
        characters: [],
        isLoading: false,
        // same as FETCH_CHARACTERS_SUCCESS, but instead of data we will show an error message
        error: action.payload,
      };
    default:
      return state;
  }
};
export default charactersReducer;
import { combineReducers } from "redux";
import { connectRouter } from "connected-react-router";
import whiskysReducer from "./whiskysReducer";
import booksReducer from "./booksReducer";
import charactersReducer from "./charactersReducer";

const rootReducer = (history) =>
  combineReducers({
    router: connectRouter(history),
    whiskies: whiskysReducer,
    books: booksReducer,
    characters: charactersReducer,
  });

export default rootReducer;
import {
  FETCH_BOOKS,
  FETCH_BOOKS_FAILURE,
  FETCH_BOOKS_SUCCESS,
} from "../actions/index";

const initialState = {
  isLoaded: false,
  books: [],
  error: null,
};

const booksReducer = (state = initialState, action) => {
  switch (action.type) {
    case FETCH_BOOKS:
      return {
        ...state,
        // whenever we want to fetch the BOOKS, set isLoading to true to show a spinner
        isLoading: true,
        error: null,
      };

    case FETCH_BOOKS_SUCCESS:
      return {
        books: [...action.payload],
        // whenever the fetching finishes, we stop showing the spinner and then show the data
        isLoading: false,
        error: null,
      };

    case FETCH_BOOKS_FAILURE:
      return {
        books: [],
        isLoading: false,
        // same as FETCH_BOOKS_SUCCESS, but instead of data we will show an error message
        error: action.payload,
      };
    default:
      return state;
  }
};
export default booksReducer;
@Sawtaytoes have updated
Evert Bouw
@evertbouw
yeah use the catchError operator instead
Evert Bouw
@evertbouw
and set the auth header in ajax.get
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))
        )
      )
    )
  );
}
Kevin Ghadyani
@Sawtaytoes
@nzsoori ^^ Look at @evertbouw's comments
nzsoori
@nzsoori
Thanks appreciate guys @evertbouw and @Sawtaytoes , that worked
Bernat Jufré Martínez
@bjufre
Hey there! Having some problems with SSR, any inputs or suggestions? I've looked around but no luck. How do I dispatch the action and wait for the epic to resolve? Thanks!
Kevin Ghadyani
@Sawtaytoes

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.

@bjufre ^^ I'm assuming you're using React. You could be using regular HTML, but it's the exact same problem.
Jose Daniel Hernandez Rios
@d4nielhdz
Has anyone used rxfire with redux-observable? I'm trying to fetch data with firestore and I want an action to be dispatched on every emission of a new value, but I'm getting the error Actions must be plain objects. Use custom middleware for async actions.
My code looks like this

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);
        })
      )
    }
    )),
  );
};
Kevin Ghadyani
@Sawtaytoes
@d4nielhdz Does Creators.fetchMenuSuccess(val) return an regular old Redux action object with a type property?
@d4nielhdz Also, what's actually giving you that error message?
Jose Daniel Hernandez Rios
@d4nielhdz
Yes, Creators.fetchMenuSuccess is a valid action. I get the error when I use pipe.
>°))))彡
@fisherspy

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'))
)
@Sawtaytoes @evertbouw
>°))))彡
@fisherspy
I have figured it out. the fetchFocusPage action has payload, if I dispatch the action without payload. Although the console will show 'fetch action coming', the process will not continue
Kevin Ghadyani
@Sawtaytoes
@d4nielhdz What's collectionData? An observable right?

@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.

>°))))彡
@fisherspy
But If I dispach{type: 'FOCUS_PAGE_FETCH' } instead of {type: 'Focus_PAGE_FETCH', payload: {}} with combineLatest. it only outputs value in my pipe the first time both observables fire once, after that if I fire one of them, it will not output values in my pipe, unless you fire both of them. with payload: {}, even just {}, combineLatest will work alright. I dont know whether it's a bug.
Pervez Alam
@pervezalam777
Can anyone share some complex Redux-Observable test-cases sample. I need to study how to write test cases which is close to real project code scenario.
Kevin Ghadyani
@Sawtaytoes
@pervezalam777
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(),
)
@fisherspy I don't think it's a bug, but it might be something wonky in your setup. If it works for you, keep it that way. But what you're seeing sounds half-correct meaning there might be something else happening.
xavier7179
@xavier7179
hi there. I already asked a portion of what I'm trying to do but I got stucked in expanding it (or better, make it "customisable"), so here I come again. The idea is: let's say that I want several epics working on ajax calls (one per epic on different urls), dealing with everythiyg (ok results, errors in calling, taking only one call and discarding duplications, taking care of canceling). Then, let's say that, for some reason, I need to concatenate them too, in order to call only one epic, and chain the aforementioned single epics (only on success, of course), keeping their code sepate not to duplicate them for the case when I need them alone, but also keeping working cancellations, error dealing and so on. Is there a way to approach the problem in a systematic way? Thank you so much!
Kevin Ghadyani
@Sawtaytoes

@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')),
)
xavier7179
@xavier7179

@Sawtaytoes sure!
The schema I'm depicting in my mind is something like (avoiding parallelism and keeping the order):

  • call doAllEpics
    • call epic1 (global_parameters)
    • if (epic1 succeed)
      • call epic2 (global_parameters)
        • if (epic2 succeed)
          • call epic3 (global_parameters)
            • ... same structure if a 4th one is needed an so on
        • else
          • do something
    • else // cancellation, errors
      • do something

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.

Julian Geppert
@Spacetoaster
Hi, jsbin live examples seem to be broken in the docs. Not sure if this has been reported already, just thought i'd post it here
Kevin Ghadyani
@Sawtaytoes
@xavier7179 What's "a third one"?
xavier7179
@xavier7179
@Sawtaytoes I cannot add a third epic (like in the above schema) to the two epics code example.
Kevin Ghadyani
@Sawtaytoes

@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
@xavier7179

@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 add originalActionPayload: 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?

Kevin Ghadyani
@Sawtaytoes
@xavier7179 Correct. You're wanting orchestration where you want one action to occur which affects another. You could simply just make a huge epic. You don't have to dispatch actions.
@xavier7179 You only need to dispatch actions when certain things happen like if other parts of your app need to know what's happening.
xavier7179
@xavier7179
But what I do not get is: I need actions to start epics, and if I want to keep each single epic apart in order to avoid duplicating the code when I need them alone, how can I implement the "once one epic end without error, start the next one" protocol?
Evert Bouw
@evertbouw
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
xavier7179
@xavier7179

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?

xavier7179
@xavier7179
For the application I'm working on, having them separate (to call alone) and find out a way to concatenate them makes totally sense... this is why I'm trying to improve the two epcs code in order to have no more boundaries.
Evert Bouw
@evertbouw
you could have the epic listen for two actions, the result action of the previous or its own start
Ahmad R
@harvester-klirk
hey guys i need help setting up "redux-persist " in a react js
Ahmad R
@harvester-klirk
i'm trying to setup redux-persist here's my code , all the tutorials on the redux-persist have a different setup approach
`const createStoreWithMiddleware = applyMiddleware('
  `promiseMiddleware,`
  `ReduxThunk`
`)(createStore);`

 `ReactDOM.render(`
  `<Provider`
   ` store={createStoreWithMiddleware(`
     ` Reducer,`
    `  window.__REDUX_DEVTOOLS_EXTENSION__ &&`
    `  window.__REDUX_DEVTOOLS_EXTENSION__()`
     `  )}`
   ` >`
     ` <BrowserRouter>`
      ` <App />`
    ` </BrowserRouter>`
   `</Provider>,` 
xavier7179
@xavier7179

@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.

André Vitor de Lima Matos
@andrevmatos
Did anyone here experience redux-observable getting slow at processing actions on some conditions? I noticed registering to some events on an EventEmitter 3rd-party lib did slow the overall state machine a lot. I'm not sure what's causing it, if it's something with schedulers. Profiling the app has shown CPU not going up, but the actions still get increasingly slow to go through certain epics
Kevin Ghadyani
@Sawtaytoes

@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?

André Vitor de Lima Matos
@andrevmatos
@Sawtaytoes the weird behavior was seen on https://github.com/raiden-network/light-client on some specific high-load scenarios. I couldn't find a small reproducible example, pretty sure it wasn't on redux-observable side and instead some slowness on the 3rd-party eventemitter lib (matrix-js-sdk). Anyway, replacing some concatMap with mergeMap + waiting observable, and replacing some 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
Kevin Ghadyani
@Sawtaytoes

@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 :).