Giter Site home page Giter Site logo

dart_redux_epics's People

Contributors

a-abramov avatar bcko avatar brianegan avatar dustin-graham avatar henge9 avatar jaggernod avatar kanelo97 avatar lisenish avatar michaelmarner avatar olehsv avatar subisu avatar tomwscott avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dart_redux_epics's Issues

Can't connect redux store

image

This is my epic class.

final githubApi = GithubApi();
  var store = Store<AppState>(
    appReducer,
    initialState: AppState(
      sharedPreferences: initInfo.sharedPreferences,
    ),
    middleware: [
      EpicMiddleware<AppState>(AppEpic(githubApi)),
    ],
  );

image

Can't dispatch the this action.

Add onChange to the EpicStore

Currently it is not possible to consume the state stream, only a current state. It makes it impossible to work with multiple streams.

Error handling in async*

Hi

I have strange problem with async* generator and exceptions handling.

Below code will work only once. I mean if i dispatch LoadDataAction twice, for the first attempt I will get LoadDataFailedAction, but on the second attempt I will get nothing, no exceptions, no _onLoadData call, just silence )
Looks like after exception everything just stopped.

Same methods but with then.catchError work like a charm

Can you help with it?

Epic<AsyncState> getEffects() {
    return combineEpics([
      new TypedEpic<AsyncState, LoadDataAction>(_onLoadData),
    ]);
  }

  Stream<Object> _onLoadData(Stream<LoadDataAction> actions, EpicStore<AsyncState> store) async* {
    await for (final action in actions) {
      try {
        final result = await _loader.load(); // always throw exception
        yield new LoadDataSuccessAction(result);
      } catch (e) {
        yield new LoadDataFailedAction(e.toString());
      }
    }
  }

Reduce boilerplate code with action type checks and casting

I think we should use same approach as with redux itself:

_reducer = combineTypedReducers([
   new ReducerBinding<State, LoadSuccessAction>(_onLoadSuccess),
   new ReducerBinding<State, LoadFailureAction>(_onLoadFailure),
]);

we can create combineTypedEpics function with similar api:

_effect = combinedTypedEpics([
  new EpicBinding<State, LoadRequestAction(_onLoadRequest)
]);

where _onLoadRequest within epic can be defined like this:

Stream<Object> _onLoadRequest(Stream<LoadRequestAction> actions, EpicStore<State> store) {
   return actions.asyncMap((action) => api.getData(...).then((data) => new LoadSuccessAction(data)));
}

or even like this:

Stream<Object> _onLoadRequest(Stream<LoadRequestAction> actions, EpicStore<State> store) async* {
  await for(final action in actions) {
    final data = await api.getData(...);
    yield new LoadSuccessAction(data);
  }
}

Currently we have to add

where((action) => action is LoadRequestAction)
.map((action) => action as LoadRequestAction)

to most epics.

We cannot use Rx Observables and ofType here because we have to merge stream with its "child" stream because of this #3.

Combining epics and normal middleware

I get an error using both types.
I don't know if is an error or maybe a don't know how to handle:
In the store initialization
middleware: middleware().addAll([new EpicMiddleware(allEpics)])
or
middleware: middleware().addAll(new EpicMiddleware(allEpics))
or
middleware: [middleware(),new EpicMiddleware(allEpics)]
I don't know how to combine both.

Epics break testing when using timers

Hi,

I might be missing something obvious here, but it seems to me that epics that include timers break tests, even if one explicitly tears down the store.

Here's the simplest possible repro:

void main() {
  group('repro', () {
    testWidgets('test', (tester) async {
      final store = Store<String>(
        (state, dynamic action) => state,
        initialState: 'Greetings!',
        middleware: [
          EpicMiddleware(_middleware),
        ],
      );

      final widget = StoreProvider<String>(
        store: store,
        child: MaterialApp(
          home: StoreConnector<String, String>(
            converter: (s) => s.state,
            builder: (context, state) => Text(state),
            // At least one dispatch is required, otherwise the epic is never initialized
            onInit: (store) => store.dispatch('whatever'),
          ),
        ),
      );

      await tester.pumpWidget(widget);

      await store.teardown();
    });
  });
}

Stream<dynamic> _middleware(Stream<dynamic> actions, EpicStore<String> store) => Observable<dynamic>(actions).debounceTime(aSecond).ignoreElements();

Running this gives:

_AssertionError ('package:flutter_test/src/binding.dart': Failed assertion: line 1050 pos 7: '_currentFakeAsync.nonPeriodicTimerCount == 0': A Timer is still pending even after the widget tree was disposed.)

I considered doing something like this:

Stream<dynamic> _middleware(Stream<dynamic> actions, EpicStore<String> store) =>
  Observable<dynamic>(actions)
    .debounceTime(aSecond)
    .ignoreElements()
    .takeUntil<dynamic>(Observable<dynamic>.fromFuture(actions.last));

But it appears as though the actions stream never completes either (even when tearing down the store).

Am I doing something wrong here?

Doesn't work with rxdart v0.24.0

Doesn't work with latest rxdart v0.24.0

Because core depends on redux_epics ^0.13.0 which depends on rxdart ^0.23.0, rxdart ^0.23.0 is required.

So, because core depends on rxdart ^0.24.0, version solving failed.
pub get failed (1; So, because core depends on rxdart ^0.24.0, version solving failed.)

Need update to redux 3.0.0

Hi, at the moment the dependencies specify that it will use Redux up to but not including Redux 3.0.0 and this is causing a problem with the dependency checker in Dart nesting too deep to try to work out a compatible version.

Can we get the version pushed up to include 3.0.0?

FWIW I have the following dependencies:

redux: ^3.0.0
rxdart: ^0.16.5
flutter_redux: ^0.5.0

Examples of Dependency Injection in Epics?

I was wondering if there was an undocumented ability to inject dependencies into an epic. The obvious example would be for injecting a different api implementation for testing but I'd also like to configure different endpoints depending on whether I'm running locally or in a live like environment.

The examples in the README hint at the concept of an api - I'm just wondering if there's a way to inject it?

...
    .asyncMap((action) => 
      // Pseudo api that returns a Future of SearchResults
      api.search((action as PerformSearch).searchTerm)
...

Happy to document it if someone can point me in the right direction...

Combineepics not working

i have on epic

Stream<dynamic> registerFetchActionEpic(Stream<dynamic> actions, EpicStore<MainState> store) {
  return actions.where((action) => action is RegisterFetchAction)
      .asyncMap((ac) => Dio().post('http://100.115.92.195:8888/create')
      .then((resches) => RegisterFetchSuccessAction())
        .catchError((error) => RegisterFetchError(error)));
}

I am trying to combine them with

var eschepischic = combineEpics<MainState>([
		registerFetchActionEpic
	]);

but

	var epicmiddleware = EpicMiddleware(eschepischic)

gives me
The instance member 'eschepischic' can't be accessed in an initializer. (Documentation) Try replacing the reference to the instance member with a different expression

rxdart 0.26

Hello! Can you update the package to be compatible with rxdart ^0.26.0 ?
redux_epics 0.14.0 depends on rxdart ^0.24.0 and no versions of redux_epics match >0.14.0 <0.15.0
redux_epics ^0.14.0 and rxdart ^0.26.0, version solving failed

Stream, not action, is being sent to reducers

Hi I am using the following code to sync to a Firebase real time database. After opening the Firebase stream, the Epic should send a new CreateTodoListFromStrings action to the reducers, which keeps the Redux TodoList synced with the database.
image

I thought it would be enough to just return on Observable (as is shown in examples here https://medium.com/shift-studio/flutter-redux-and-firebase-cloud-firestore-in-sync-2c1accabdac4) and it was resolved into an action by the time it reaches the reducer. Other examples, including this repo's ReadMe, are returning a stream or Observable, so I assumed this pattern would work.

But according to the debugger, the reducers are receiving the Observable itself. As a result none of the reducers are activated, and the store is never updated.
image

Am I not thinking about this the right way?

Type mismatch issue

I am trying to sync my Firestore with Redux. I am using the following:

  redux: "^3.0.0"
  flutter_redux: "^0.5.0"
  redux_logging: "^0.3.0"
  redux_epics: "^0.9.0"
  rxdart: "^0.16.7"

When I try to add my epics to the middleware I get this error on this line of code:

final allEpics = combineEpics<AppState>([
    ordersEpic,
  ]);

[dart] The element type '(Stream, EpicStore) → Stream' can't be assigned to the list type '(Stream, EpicStore) → Stream'.

enter image description here

Epic:

Stream<dynamic> ordersEpic(Stream<OrdersOnDataEventAction> actions, EpicStore<AppState> store){
  return Observable(actions)
  .ofType(TypeToken<RequestOrdersDataEventsAction>())
  .switchMap((RequestOrdersDataEventsAction requestOrders){
    return getOrders()
    .map((orders)=>OrdersOnDataEventAction(orders))
    .takeUntil(actions.where((action)=>action is CancelOrdersDataEventsAction));
  });
}
Observable<List<DocumentSnapshot>> getOrders(){
  return Observable(Firestore.instance.collection("orders").snapshots())
  .map((QuerySnapshot q)=>q.documents);
}

Actions:

class RequestOrdersDataEventsAction{}

class CancelOrdersDataEventsAction{}

class OrdersOnDataEventAction{ 
  final orders;
  OrdersOnDataEventAction(this.orders);
 }

Orders:

final ordersReducer = combineReducers<List<DocumentSnapshot>>([
  TypedReducer<List<DocumentSnapshot>,OrdersOnDataEventAction>(_setOrders)
]);

List<DocumentSnapshot> _setOrders(List<DocumentSnapshot> oldOrders, OrdersOnDataEventAction action){
  return action.orders;
}

Note I have also tried Stream<dynamic> ordersEpic(Stream<dynamic> actions, EpicStore<AppState> store)

and I get the same error
The element type '(Stream<dynamic>, EpicStore<AppState>) → Stream<dynamic> can't be assigned to the list type '(Stream<dynamic>, EpicStore<AppState>) → Stream<dynamic>

This issue is also referenced on SO here

How to dispatch multiple actions from an Epic

Is there a pattern to dispatch multiple actions from an Epic?

return Observable(actions)
        .ofType(TypeToken<SearchAction>())
        .debounce(Duration(milliseconds: 300))
        .switchMap((SearchAction action) {
      return _getResults(store, action)
          .map((result) => OnSearchResult(result))
        //want to dispatch a analytics action here something like
        // .map((result) => [OnSearchResult(result), SearchAnalyticAction(result)])
          .takeUntil(actions.where((action) => action is CancelSearchAction));

is there a way to do that? Or is better to have 2 separeted epics listen same action (SearchAction)?

tks

Release 1.x

Can we get a 1.x release so updating consumers is easier?

Unhandled Exception: type 'LoadAccountErrorAction' is not a subtype of type 'FutureOr<LoadAccountCompleteAction>'

I'm using dart_redux_epics, but catcherror can not return error actions
Epics:

Stream<dynamic> loadAccountEpic(Stream<dynamic> actions, EpicStore<AppState> store) {
return new Observable(actions).
ofType(new TypeToken<LoadAccountAction>())
.flatMap((action) {
    return new Observable.fromFuture(NetUtils.getInstance()
        .userInfo(pb.UserInfoRequest())
        .then((res) => LoadAccountCompleteAction(res.user))
        .catchError((error) => LoadAccountErrorAction(error)));
  });

Actions:

class LoadAccountAction {
  LoadAccountAction();
}

class LoadAccountCompleteAction {
  User user;

  LoadAccountCompleteAction(this.user);

  @override
  String toString() {
    return 'LoadAccountCompleteAction{user: $user}';
  }
}

class LoadAccountErrorAction {
  final dynamic error;

  LoadAccountErrorAction(this.error);

  @override
  String toString() {
    return 'LoadAccountErrorAction{error: $error}';
  }
}

Errors:

[VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: type 'LoadAccountErrorAction' is not a subtype of type 'FutureOr<LoadAccountCompleteAction>'

Multiple streams with replace and cancel

Hi,
I'm using redux_epics with rxDart 0.26.0 and trying to achieve the following:

Making a multiple groups chat app. Need to start streaming chat (firestore collection) on every chat group created. Also cancel the stream when the group is deleted, or on signout.

Stream<dynamic> streamGroupChat(
    Stream<dynamic> actions, EpicStore<AppState> store) {
  return actions
      .where((action) =>
          action is LoadGroupSucceededAction && action.group != null)
      .flatMap((action) {
    return _streamChat(action.group!.id!)
        .takeUntil(actions
            .where((action) => action is ClearChatsByGroupAction)
            .where((newAction) => newAction.groupId == action.group!.id!))
        .map((e) {
      return LoadChatsSucceededAction(e);
    });
  });
}

The problem i'm facing is:
When I use flatMap, i get all the chat streams loaded into store...but the takeUntil (Cancellation) does not work.
If I change the flapMap to switchMap, takeUntil starts working but only the last LoadGroupSucceededAction is streamed (overwrites the previous ones).
I thought mergeMap was the key but can't find that in RxDart.

How do I add (flatMap) multiple streams for new groups, replace (switchMap) existing stream for reloaded group, and cancel (takeUntil) streams?

Thanks.

Performance issue of Future.delayed hack

This line:
https://github.com/brianegan/dart_redux_epics/blob/master/lib/src/epic_middleware.dart#L50

runs timer on every action dispatched within store. In our app it adds visible performance bottleneck. Can you please add option to dispatch actions immediately (can be done backward-compatible)? We could use this option and do not use async* actions instead.

We are still using 0.7.x version and we will use it for few months from now (dart 2.0 migration). Can you also please publish this option for latest 0.7.x package as well?

How to create an epic that does not dispatch an Action

Hi, first of all thanks for the library!

I'm still new to flutter and I am trying to take some concepts over from Angular (Ngrx) and was stuck on how to create epics that does not dispatch any actions.

In Ngrx you would have to write an effect with dispatch = false, I was wondering if I can achieve the same behavior using epics in flutter.

Thanks in advance!

Epic `store.dispatch` workflow

Hi
I have code (just example):

Stream<Object> _onSomeAction(Stream<Object> actions, EpicStore<AppState> store) {
    return actions.where((action) => action is SomeAction).asyncMap(
          (action) => _api.someAction()
              .then((result) => new AnotherAction()),
        );
  }

It works well if I want to handle AnotherAction only in reducers. But very often I want to handle this action in another Epic(s). For example:

Stream<Object> _onAnotherAction(Stream<Object> actions, EpicStore<AppState> store) {
    return actions.where((action) => action is AnotherAction).asyncMap(
          (action) => _api.anotherAction()
              .then((result) => new FinalAction()),
        );
  }

It will not work as is. To fix it I can merge streams from both Epics:

Stream<Object> _onSomeAction(Stream<Object> actions, EpicStore<AppState> store) {
    final stream = actions.where((action) => action is SomeAction).asyncMap(
          (action) => _api.someAction()
              .then((result) => new AnotherAction()),
        );

    return new MergeStream([stream, _onAnotherAction(stream, store)]);
  }

But IMHO it complicates the readability of the code and add additional complexity.
I tried to dispatch action directly from Epic:

Stream<Object> _onSomeAction(Stream<Object> actions, EpicStore<AppState> store) {
    return actions.where((action) => action is SomeAction).asyncMap(
          (action) => _api.someAction()
              .then((result) => dispatcher.dispatch(new AnotherAction())), // dispatcher - just a wrapper for store.dispatch
        );
  }

and it work like a charm! Now I can handle AnotherAction in reducers and in Epics.
But I found bellow comment in EpicStore sources:

/// Due to the way streams are implemented with Dart, it's impossible to
/// perform `store.dispatch` from within an [Epic] or observe the store directly.

if I understood correctly this comment solution with direct dispatch call should not work but it works.

That is why I have few questions:

  1. What does this comment mean?
  2. Can I continue to use dispatch in Epics without any undesirable consequences?
  3. Why Epics doesn't support this scenario without any MergeStream by default?

Thanks!

Unhandled Exception: type 'SetEventItemFailAction' is not a subtype of type 'FutureOr<UpdateTaskAction>'

Here is my code:

Stream<dynamic> setEventItemsEffect(
    Stream<dynamic> actions, EpicStore<AppState> store) {
  return actions
      .whereType<SetEventItemAction>()
      .asyncMap((action) => handleEventItems(action.task)
        .then((res) => UpdateTaskAction(task: action.task)
        .catchError((_) => SetEventItemFailAction(task: action.task))
  );
}

When to catchError, this code get error Unhandled Exception: type 'SetEventItemFailAction' is not a subtype of type 'FutureOr<UpdateTaskAction>'

When I change code to this according to #30 :

Stream<dynamic> setEventItemsEffect(
    Stream<dynamic> actions, EpicStore<AppState> store) {
  return actions
      .whereType<SetEventItemAction>()
      .asyncMap((action) => Stream<dynamic>.fromFuture(handleEventItems(action.task)
        .then((res) => UpdateTaskAction(task: action.task)
        .catchError((_) => SetEventItemFailAction(task: action.task)))
  );
}

The error is gone, but the type goes like this.
Action: Instance of '_ControllerStream<dynamic>'

or:

Stream<dynamic> setEventItemsEffect(
    Stream<dynamic> actions, EpicStore<AppState> store) {
  return actions
      .whereType<SetEventItemAction>()
      .asyncMap((action) => Stream<UpdateTaskAction>.fromFuture(handleEventItems(action.task)
        .then((res) => UpdateTaskAction(task: action.task)
        .catchError((_) => SetEventItemFailAction(task: action.task)))
  );
}

The type goes like this.
Action: Instance of '_ControllerStream<dynamic>'

Upgrading rxdart to the latest

Is there a technical reason why rxdart is limited to 0.14.x? Can it be upgraded without a major haul?

We're ok doing the work. I just wanted to check.

takeUntil action make stream unusable the second time

I am using flutter with redux and for handling stream data I'm using redux-epics.

Like this:

Stream<dynamic> getDataEpic(Stream<dynamic> actions, EpicStore<AppState> store) {
  return Observable(actions)
      .ofType(TypeToken<RequestDataAction>())
      .flatMap((RequestDataAction action) {
        return getDataStream()
          .map(
              (data) => UpdateStateWithData(data)
          );
      })
      .takeUntil(actions.where((action) => action is CancelGetDataAction));
}

// getDataStream() - is a stream that returns some stuff... 

In my screen in onInit I call store.dispatch(RequestDataAction()) and onDispose I call store.dispatch(CancelGetDataAction()) which destroys the Observable altogether, so the next time I go to this screen if I call store.dispatch(RequestDataAction()) my stream is not sending data, actually the entire Observable is dead!

How can I solve this problem? As far as I can see the problem is takeUntil because I completely closes the observable..

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.