{"id":17238366,"url":"https://github.com/hoc081098/rx_redux","last_synced_at":"2025-10-08T06:51:08.766Z","repository":{"id":34898294,"uuid":"187627504","full_name":"hoc081098/rx_redux","owner":"hoc081098","description":"🍄 Reactive redux store for Dart \u0026 Flutter 🌰 Redux implementation based on Dart Stream, with the power of RxDart","archived":false,"fork":false,"pushed_at":"2025-09-04T18:58:44.000Z","size":860,"stargazers_count":16,"open_issues_count":10,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-04T20:43:03.364Z","etag":null,"topics":["flutter-bloc","flutter-bloc-rxdart","flutter-reactive","flutter-redux","flutter-redux-demo","flutter-rx","flutter-rxdart","reactive-redux","redux-observable","redux-rx","redux-store","rx-redux","rxdart","rxdart-bloc","rxdart-epic","rxdart-flutter","rxdart-helper","rxdart-redux","rxredux"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/rx_redux","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hoc081098.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-05-20T11:28:29.000Z","updated_at":"2023-08-08T17:13:50.000Z","dependencies_parsed_at":"2024-04-09T21:44:00.866Z","dependency_job_id":"eba86f33-0378-40c4-8f05-3f01006876d0","html_url":"https://github.com/hoc081098/rx_redux","commit_stats":{"total_commits":161,"total_committers":3,"mean_commits":"53.666666666666664","dds":"0.012422360248447228","last_synced_commit":"ff8b4797e40588be87aafae486cfc9ff07041238"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/hoc081098/rx_redux","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoc081098%2Frx_redux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoc081098%2Frx_redux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoc081098%2Frx_redux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoc081098%2Frx_redux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hoc081098","download_url":"https://codeload.github.com/hoc081098/rx_redux/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoc081098%2Frx_redux/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278903007,"owners_count":26065786,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-08T02:00:06.501Z","response_time":56,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flutter-bloc","flutter-bloc-rxdart","flutter-reactive","flutter-redux","flutter-redux-demo","flutter-rx","flutter-rxdart","reactive-redux","redux-observable","redux-rx","redux-store","rx-redux","rxdart","rxdart-bloc","rxdart-epic","rxdart-flutter","rxdart-helper","rxdart-redux","rxredux"],"created_at":"2024-10-15T05:45:26.514Z","updated_at":"2025-10-08T06:51:08.749Z","avatar_url":"https://github.com/hoc081098.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rx_redux\n\n## Author: [Petrus Nguyễn Thái Học](https://github.com/hoc081098)\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://github.com/hoc081098/rx_redux/blob/v2/images/logo.png?raw=true\" alt=\"Logo\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"#\"\u003e\u003cimg src=\"https://github.com/hoc081098/rx_redux/workflows/Dart%20CI/badge.svg\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://travis-ci.org/hoc081098/rx_redux\"\u003e\u003cimg src=\"https://travis-ci.org/hoc081098/rx_redux.svg?branch=master\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://pub.dev/packages/rx_redux\"\u003e\u003cimg src=\"https://img.shields.io/pub/v/rx_redux.svg\"/\u003e\u003c/a\u003e\n    \u003ca href=\"https://codecov.io/gh/hoc081098/rx_redux\"\u003e\u003cimg src=\"https://codecov.io/gh/hoc081098/rx_redux/branch/master/graph/badge.svg\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\u003cimg src=\"https://img.shields.io/github/license/hoc081098/rx_redux?color=4EB1BA\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/dart-lang/pedantic\"\u003e\u003cimg src=\"https://img.shields.io/badge/style-pedantic-40c4ff.svg\" /\u003e\u003c/a\u003e\n    \u003ca href=\"https://app.codacy.com/manual/hoc081098/rx_redux?utm_source=github.com\u0026utm_medium=referral\u0026utm_content=hoc081098/rx_redux\u0026utm_campaign=Badge_Grade_Dashboard\"\u003e\n        \u003cimg src=\"https://api.codacy.com/project/badge/Grade/7ecf57bd3c634804a7f596b4f3b35b30\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://hits.seeyoufarm.com\"\u003e\n            \u003cimg src=\"https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fhoc081098%2Frx_redux\u0026count_bg=%2379C83D\u0026title_bg=%23555555\u0026icon=\u0026icon_color=%23E7E7E7\u0026title=rx_redux\u0026edge_flat=false\"/\u003e\n    \u003c/a\u003e\n\n\u003c/p\u003e\n\n-   Reactive redux store for `Dart` \u0026 `Flutter` inspired by **[RxRedux-freeletics](https://github.com/freeletics/RxRedux)**\n\n-   A Redux store implementation entirely based on Dart `Stream`, with the power of `RxDart` (inspired by [redux-observable](https://redux-observable.js.org)) \nthat helps to isolate side effects. RxRedux is (kind of) a replacement for RxDart's `.scan()` operator. \n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/freeletics/RxRedux/master/docs/rxredux.png\" width=\"600\" alt=\"RxRedux In a Nutshell\"/\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://github.com/hoc081098/rx_redux/blob/v2/images/counter.png?raw=true\" height=\"600\"\u003e\n\u003c/p\u003e\n\n## Get started\n\n```yaml\ndependencies:\n  rx_redux: ^2.0.0\n```\n\n## How is this different from other Redux implementations\nIn contrast to any other Redux inspired library out there, this library is pure backed on top of Dart Stream.\nThis library offers a custom stream transformer `ReduxStoreStreamTransformer` (or extension method `reduxStore`) and treats upstream events as `Actions`. \n\n## Basic concepts\n### Redux Store\n-   A Store is basically an stream container for state. \nThis library offers a custom stream transformer `ReduxStoreStreamTransformer` (or extension method `reduxStore`) to create such a state container.\nIt takes an `initialState` and a list of `SideEffect\u003cState, Action\u003e` and a `Reducer\u003cState, Action\u003e`.\n\n-   Since version 2.x, add `RxReduxStore` class, built for Flutter UI.\n\n### Action\nAn Action is a command to \"do something\" in the store. \nAn `Action` can be triggered by the user of your app (i.e. UI interaction like clicking a button) but also a `SideEffect` can trigger actions.\nEvery Action goes through the reducer. \nIf an `Action` is not changing the state at all by the `Reducer` (because it's handled as a side effect), just return the previous state.\nFurthermore, `SideEffects` can be registered for a certain type of `Action`.\n\n### Reducer\nA `Reducer` is basically a function `(State, Action) -\u003e State` that takes the current State and an Action to compute a new State.\nEvery `Action` goes through the state reducer.\nIf an `Action` is not changing the state at all by the `Reducer` (because it's handled as a side effect), just return the previous state.\n\n### Side Effect\nA Side Effect is a function of type `(Stream\u003cAction\u003e, GetState\u003cState\u003e) -\u003e Stream\u003cAction\u003e`.\n**So basically it's Actions in and Actions out.** \nYou can think of a `SideEffect` as a use case in clean architecture: It should do just one job.\nEvery `SideEffect` can trigger multiple `Actions` (remember it returns `Stream\u003cAction\u003e`) which go through the `Reducer` but can also trigger other `SideEffects` registered for the corresponding `Action`.\nAn `Action` can also have a `payload`. For example, if you load some data from backend, you emit the loaded data as an `Action` like `class DataLoadedAction { final Foo data; }`. \nThe mantra an Action is a command to do something is still true: in that case it means data is loaded, do with it \"something\".t\n\n### GetState\nWhenever a `SideEffect` needs to know the current State it can use `GetState` to grab the latest state from Redux Store.\n`GetState` is basically just a function `() -\u003e State` to grab the latest State anytime you need it.\n\n### Selector\n\nInspirited by [NgRx memoized selector](https://ngrx.io/guide/store/selectors)\n-   Selectors can compute derived data, allowing Redux to store the minimal possible state.\n    \n-   Selectors are efficient. A selector is not recomputed unless one of its arguments changes.\n\n-   When using the `select`, `select2` to `select9`, `selectMany` functions, \n    keeps track of the latest arguments in which your selector function was invoked. \n    Because selectors are pure functions, the last result can be returned \n    when the arguments match without re-invoking your selector function. \n    This can provide performance benefits, particularly with selectors that perform expensive computation. \n    This practice is known as memoization.\n\n## Usage\n\n### Version 2.x: Prefer to use `RxReduxStore` over `ReduxStoreStreamTransformer`, but have same concept as version 1.x\n\n```dart\nfinal store = RxReduxStore(\n  initialState: ViewState([]),\n  sideEffects: [addTodoEffect, removeTodoEffect, toggleTodoEffect],\n  reducer: reducer,\n  logger: rxReduxDefaultLogger,\n);\n\nstore.stateStream.listen((event) =\u003e print('~\u003e State : $event'));\nstore.actionStream.listen((event) =\u003e print('~\u003e Action: $event'));\nstore.dispatch(Action(Todo(i, 'Title $i', i.isEven), ActionType.add));\nawait store.dispose();\n```\n\n### Note: below is the documentation for version 1.x, but have same concept as version 2.x\n\nLet's create a simple Redux Store for Pagination: Goal is to display a list of `Persons` on screen.\n**For a complete example check [the sample application incl. README](example/README.md)**\nbut for the sake of simplicity let's stick with this simple \"list of persons example\":\n\n#### 1. Define `State` and `initialState`\n\n```dart\nclass State {\n  final int currentPage;\n  final List\u003cPerson\u003e persons; // The list of persons \n  final bool loadingNextPage;\n  final errorLoadingNextPage;\n  // constructor\n  // hashCode and ==\n  // copyWith\n}\n\nfinal initialState = State(\n  currentPage: 0, \n  persons: [], \n  loadingNextPage: false, \n  errorLoadingNextPage: null,\n);\n```\n\n#### 2. Define `Actions`\n\n```dart\nabstract class Action { }\n\n// Action to load the first page. Triggered by the user.\nclass LoadNextPageAction implements Action {\n  const LoadNextPageAction();\n}\n\n// Persons has been loaded\nclass PageLoadedAction implements Action {\n  final List\u003cPerson\u003e personsLoaded;\n  final int page;\n  // constructor\n}\n\n// Started loading the list of persons\nclass LoadPageAction implements Action {\n  const LoadPageAction();\n}\n\n// An error occurred while loading\nclass ErrorLoadingNextPageAction implements Action {\n  final error;\n  // constructor\n}\n```\n\n#### 3. Define `SideEffects`\n\n```dart\n// SideEffect is just a type alias for such a function:\nStream\u003cState\u003e loadNextPageSideEffect (\n  Stream\u003cAction\u003e actions,\n  GetState\u003cState\u003e state,\n) =\u003e\n  actions\n    // This side effect only runs for actions of type LoadNextPageAction\n    .whereType\u003cLoadNextPageAction\u003e()\n    .switchMap((_) {\n      // do network request\n      final State currentState = state();\n      final int nextPage = state.currentPage + 1;\n      \n      return backend\n        .getPersons(nextPage)\n        .map\u003cAction\u003e((List\u003cPerson\u003e person) {\n          return PageLoadedAction(\n            personsLoaded: persons, \n            page: nextPage\n          );\n        })\n        .onErrorReturnWith((error) =\u003e ErrorLoadingNextPageAction(error))\n        .startWith(const LoadPageAction());\n    });\n```\n\n#### 4. Define `Reducer`\n\n```dart\n// Reducer is just a type alias for a function\nState reducer(State state, Action action) {\n  if (action is LoadPageAction) {\n    return state.copyWith(loadingNextPage: true);\n  }\n  if (action is ErrorLoadingNextPageAction) {\n    return state.copy(\n      loadingNextPage: false,\n      errorLoadingNextPage: action.error,\n    );\n  }\n  if (action is PageLoadedAction) {\n    return state.copy(\n      loadingNextPage: false, \n      errorLoadingNextPage: null\n      persons: [...state.persons, ...action.persons],\n      page: action.page,\n    );\n  }\n\n  // Reducer is actually not handling this action (a SideEffect does it)\n  return state;\n}\n```\n\n#### 5. Combine all it into one\n\n-   Using `ReduxStoreStreamTransformer`:\n\n```dart\nfinal Stream\u003cAction\u003e actions = PublishSubject\u003cAction\u003e();\nfinal List\u003cSideEffect\u003cState, Action\u003e sideEffects = [loadNextPageSideEffect, ...];\n\nactions.transform(\n  ReduxStoreStreamTransformer\u003cAction, State\u003e(\n    initialStateSupplier: () =\u003e initialState,\n    sideEffects: sideEffects,\n    reducer: reducer,\n  ),\n).listen(view.render);\n```\n\n-   Using extension method `reduxStore`:\n\n```dart\nactions.reduxStore(\n  initialStateSupplier: () =\u003e initialState,\n  sideEffects: sideEffects,\n  reducer: reducer,\n).listen(view.render);\n```\n\n-   Using `RxReduxStore`:\n```dart\nfinal store = RxReduxStore(\n  initialState: initialState,\n  sideEffects: sideEffects,\n  reducer: reducer,\n  logger: rxReduxDefaultLogger,\n  errorHandler: (error, st) =\u003e print('$error, $st'),\n);\nstore.stateStream.listen(view.render);\n\nAction action = ...;\nstore.dispatch(action);\n```\n\n#### 6. More details\n\nThe [following video](https://youtu.be/M7lx9Y9ANYo) (click on it) illustrates the workflow:\n\n[![RxRedux explanation](https://i.ytimg.com/vi/M7lx9Y9ANYo/hqdefault.jpg?sqp=-oaymwEXCNACELwBSFryq4qpAwkIARUAAIhCGAE=\u0026rs=AOn4CLAqwunKP2_qGE0HYUlquWkFccM5MA)](https://youtu.be/M7lx9Y9ANYo)\n\n0.  Let's take a look at the following illustration:\nThe blue box is the `View` (think UI). \nThe `Presenter` or `ViewModel` has not been drawn for the sake of readability but you can think of having such additional layers between View and Redux State Machine.\nThe yellow box represents a `Store`. \nThe grey box is the `reducer`. \nThe pink box is a `SideEffect`\nAdditionally, a green circle represents `State` and a red circle represents an `Action` (see next step).\nOn the right you see a UI mock of a mobile app to illustrate UI changes.\n\n1.  `NextPageAction` gets triggered from the UI (by scrolling at the end of the list). Every `Action` goes through the `reducer` and all `SideEffects` registered for this type of Action.\n\n2.  `Reducer` is not interested in `NextPageAction`. So while `NextPageAction` goes through the reducer, it doesn't change the state.\n\n3.  `loadNextPageSideEffect` (pink box), however, cares about `NextPageAction`. This is the trigger to run the side-effect.\n\n4.  So `loadNextPageSideEffect` takes `NextPageAction` and starts doing the job and makes the http request to load the next page from backend. Before doing that, this side effect starts with emitting `LoadPageAction`.\n\n5.  `Reducer` takes `LoadPageAction` emitted from the side effect and reacts on it by \"reducing the state\". \nThis means `Reducer` knows how to react on `LoadPageAction` to compute the new state (showing progress indicator at the bottom of the list).\nPlease note that the state has changed (highlighted in green) which also results in changing the UI (progress indicator at the end of the list).\n\n6.  Once `loadNextPageSideEffect` gets the result back from backend, the side effect emits a new `PageLoadedAction`.\nThis Action contains a \"payload\" - the loaded data.\n\n```dart\nclass PageLoadedAction implements Action {\n  final List\u003cPerson\u003e personsLoaded;\n  final int page;\n}\n```\n\n7.  As any other Action `PageLoadedAction` goes through the `Reducer`. The Reducer processes this Action and computes a new state out of it by appending the loaded data to the already existing data (progress bar also is hidden).\n\nFinal remark:\nThis system allows you to create a plugin in system of `SideEffects` that are highly reusable and specific to do a single use case.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/freeletics/RxRedux/master/docs/step13.png\" width=\"640\" alt=\"Step12\" /\u003e\n\u003c/p\u003e\n\nAlso `SideEffects` can be invoked by `Actions` from other `SideEffects`.\n\n**For a complete example check [the sample application incl. README](example/README.md)**\n\n## Examples\n\n| [Pagination list (load more) (endless scrolling)](https://github.com/hoc081098/load_more_flutter_BLoC_pattern_RxDart_and_RxRedux/tree/master/lib/pages/rx_redux) | [Flutter github search using rx_redux](https://github.com/hoc081098/flutter_github_search_rx_redux) |  \n| ------------- | ------------- |\n| \u003cimg src=\"https://github.com/hoc081098/hoc081098.github.io/blob/master/load_more/rx_redux_screen.gif?raw=true\" height=\"480\"\u003e                                     | \u003cimg src=\"https://github.com/hoc081098/hoc081098.github.io/blob/master/flutter_github_search_rx_redux/demo.gif?raw=true\" height=\"480\"\u003e |\n\n## FAQ\n\n### I get a `StackOverflowError`\nThis is a common pitfall and is most of the time caused by the fact that a `SideEffect` emits an `Action` as output that it also consumes from upstream leading to an infinite loop.\n\n```dart\n\nfinal SideEffect\u003cState, Int\u003e sideEffect = (actions, state) =\u003e actions.map((i) =\u003e i * 2);\n\nfinal inputActions = Stream.value(1);\n\ninputActions.reduxStore(\n  initialStateSupplier: () =\u003e 'InitialState',\n  sideEffects: [sideEffect],\n  reducer: (state, action) =\u003e newState,\n);\n```\n\nThe problem is that from upstream we get `Int 1`.\nBut since `SideEffect` reacts on that action `Int 1` too, it computes `1 * 2` and emits `2`, which then again gets handled by the same SideEffect ` 2 * 2 = 4` and emits `4`, which then again gets handled by the same SideEffect `4 * 2 = 8` and emits `8`, which then getst handled by the same SideEffect and so on (endless loop) ...\n\n### Who processes an `Action` first: `Reducer` or `SideEffect`\n\nSince every Action runs through both `Reducer` and registered `SideEffects` this is a valid question.\nTechnically speaking `Reducer` gets every `Action` from upstream before the registered `SideEffects`.\nThe idea behind this is that a `Reducer` may have already changed the state before a `SideEffect` start processing the Action.\n\nFor example let's assume upstream only emits exactly one Action (because then it's simpler to illustrate the sequence of workflow):\n\n```dart\n// 1. upstream emits events\nfinal upstreamActions = Stream.value(SomeAction());\n\nSideEffect\u003cState, Action\u003e sideEffect1 = (actions, state) {\n  // 3. Runs because of SomeAction\n  return actions.where((a) =\u003e a is SomeAction).mapTo(OtherAction());\n};\n\nSideEffect\u003cState, Action\u003e sideEffect2 = (actions, state) {\n  // 5. Runs because of OtherAction\n  return actions.where((a) =\u003e a is OtherAction).mapTo(YetAnotherAction());\n};\n\nupstreamActions.reduxStore(\n  initialStateSupplier: () =\u003e initialState,\n  sideEffects: [sideEffect1, sideEffect2],\n  reducer: (state, action) {\n    // 2. This runs first because of SomeAction\n    ...\n    // 4. This runs again because of OtherAction (emitted by SideEffect1)\n    ...\n    // 6. This runs again because of YetAnotherAction emitted from SideEffect2)\n  }\n).listen(print);\n```\n\nSo the workflow is as follows:\n1.  Upstream emits `SomeAction`\n2.  `reducer` processes `SomeAction`\n3.  `SideEffect1` reacts on `SomeAction` and emits `OtherAction` as output\n4.  `reducer` processes `OtherAction`\n5.  `SideEffect2` reacts on `OtherAction` and emits `YetAnotherAction`\n6.  `reducer` processes `YetAnotherAction`\n\n### Can I use `variable` and `function` for `SideEffects` or `Reducer`\n\nAbsolutely. `SideEffect` is just a type alias for a function `typedef Stream\u003cA\u003e SideEffect\u003cS, A\u003e(Stream\u003cA\u003e actions, GetState\u003cS\u003e state);`.\n\nIn `Dart` you can use a lambda for that like this:\n```dart\nSideEffect\u003cState, Action\u003e sideEffect1 = (actions, state) {\n  return actions\n      .where((a) =\u003e a is SomeAction)\n      .mapTo(OtherAction());\n};\n```\n\nof write a function (instead of a lambda):\n\n```dart\nStream\u003cAction\u003e sideEffect2(\n  Stream\u003cAction\u003e actions,\n  GetState\u003cState\u003e state,\n) {\n  return actions\n      .where((a) =\u003e a is SomeAction)\n      .mapTo(OtherAction());\n}\n```\n\nBoth are totally equal and can be used like that:\n\n```dart\nupstreamActions.reduxStore(\n  initialStateSupplier: () =\u003e initialState,\n  sideEffects: [sideEffect1, sideEffect2],\n  reducer: (state, action) =\u003e newState,\n).listen(...);\n```\n\nThe same is valid for Reducer. Reducer is just a type alias for a function `typedef S Reducer\u003cS, A\u003e(S currentState, A newAction);`\nYou can define your reducer as lambda or function:\n\n```dart\nfinal reducer = (State state, Action action) =\u003e /*return new state*/;\n\n// or\n\nState reducer(State state, Action action) {\n  // return new state\n}\n```\n\n### Is `distinct` (More commonly known as `distinctUntilChanged` in other Rx implementations) considered as best practice\nYes it is because `reduxStore(...)` is not taking care of only emitting state that has been changed\ncompared to previous state.\nTherefore, `.distinct()` is considered as best practice.\n```dart\nactions\n  .reduxStore( ... )\n  .distinct()\n  .listen(view.render);\n```\n\n### What if I would like to have a SideEffect that returns no Action\n\nFor example, let's say you just store something in a database, but you don't need a Action as result\npiped backed to your redux store. In that case you can simple use `Stream.empty()` like this:\n\n```dart\nStream\u003cAction\u003e saveToDatabaseSideEffect(Stream\u003cAction\u003e actions, GetState\u003cState\u003e getState) {\n    return actions.flatMap((_) async* {\n      await saveToDb(something);\n      // not emit any Action\n    });\n}\n```\n\n### How do I cancel ongoing `SideEffects` if a certain `Action` happens\n\nLet's assume you have a simple `SideEffect` that is triggered by `Action1`. \nWhenever `Action2` is emitted our `SideEffect` should stop. \nIn `RxDart` this is quite easy to do by using: `.takeUntil()`\n\n```dart\nmySideEffect(Stream\u003cAction\u003e actions, GetState\u003cState\u003e getState) =\u003e \n    actions\n        .whereType\u003cAction1\u003e()\n        .flatMap((_) =\u003e doSomething())\n        .takeUntil(actions.whereType\u003cAction2\u003e()); // Once Action2 triggers the whole SideEffect gets canceled.\n```\n\n### Do I need an Action to start observing data\nLet's say you would like to start observing a database right from the start inside your Store.\nThis sounds pretty much like as soon as you have subscribers to your Store and therefore you don't need a dedicated Action to start observing the database.\n\n```dart\nStream\u003cAction\u003e observeDatabaseSideEffect(Stream\u003cAction\u003e _, GetState\u003cState\u003e __) =\u003e\n    database // please notice that we don't use Stream\u003cAction\u003e at all\n        .queryItems()\n        .map((items) =\u003e DatabaseLoadedAction(items));\n```\n\n## Features and bugs\n\nPlease file feature requests and bugs at the [issue tracker][tracker].\n\n[tracker]: https://github.com/hoc081098/rx_redux/issues\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoc081098%2Frx_redux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhoc081098%2Frx_redux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoc081098%2Frx_redux/lists"}