{"id":21519917,"url":"https://github.com/mrnkr/redux_toolkit","last_synced_at":"2025-04-09T22:11:30.018Z","repository":{"id":56838021,"uuid":"282717654","full_name":"mrnkr/redux_toolkit","owner":"mrnkr","description":"Dart port of the official, opinionated, batteries-included toolset for efficient Redux development","archived":false,"fork":false,"pushed_at":"2021-01-17T14:36:59.000Z","size":240,"stargazers_count":7,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-09T22:11:21.131Z","etag":null,"topics":["dart","dart-port","flutter","redux","redux-toolkit","toolset"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/redux_toolkit","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/mrnkr.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}},"created_at":"2020-07-26T19:20:33.000Z","updated_at":"2022-03-24T06:52:52.000Z","dependencies_parsed_at":"2022-09-12T11:51:24.471Z","dependency_job_id":null,"html_url":"https://github.com/mrnkr/redux_toolkit","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrnkr%2Fredux_toolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrnkr%2Fredux_toolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrnkr%2Fredux_toolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mrnkr%2Fredux_toolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mrnkr","download_url":"https://codeload.github.com/mrnkr/redux_toolkit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248119294,"owners_count":21050755,"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","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":["dart","dart-port","flutter","redux","redux-toolkit","toolset"],"created_at":"2024-11-24T01:00:44.620Z","updated_at":"2025-04-09T22:11:29.998Z","avatar_url":"https://github.com/mrnkr.png","language":"Dart","readme":"# redux_toolkit\n\n[![pub package](https://img.shields.io/pub/v/redux_toolkit.svg)](https://pub.dartlang.org/packages/redux_toolkit)\n[![license](https://img.shields.io/github/license/mrnkr/redux_toolkit)](https://github.com/mrnkr/redux_toolkit/blob/master/LICENSE)\n[![Build Status](https://travis-ci.com/mrnkr/redux_toolkit.svg?branch=master)](https://travis-ci.com/mrnkr/redux_toolkit)\n[![codecov](https://codecov.io/gh/mrnkr/redux_toolkit/branch/master/graph/badge.svg)](https://codecov.io/gh/mrnkr/redux_toolkit)\n\nDart port of the official, opinionated, batteries-included toolset for efficient Redux development. Do check out the original [`redux-toolkit`](https://redux-toolkit.js.org/) to see what this lib is inspired on.\n\n## Store setup\n\nA friendly abstraction over the standard way of instantiating the `Store` class. It aims to provide good defaults to provide a smoother experience for us developers.\n\nThe defaults are:\n\n- [`redux-thunk`](https://github.com/brianegan/redux_thunk) as the standard way to handle async operations.\n- Readily available [`redux_dev_tools`](https://github.com/brianegan/redux_dev_tools) and [`redux_remote_devtools`](https://github.com/MichaelMarner/dart-redux-remote-devtools)\n\n```dart\nfinal store = await configureStore\u003cAppState\u003e((builder) {\n  builder.withReducer(reducer);\n  builder.withPreloadedState(AppState.initialState());\n\n  if (Config.reduxDevtoolsEnabled) {\n    builder.usingDevtools(Config.reduxDevtoolsUrl);\n  }\n});\n```\n\n### API Reference\n\n```dart\nStore\u003cState\u003e createStore\u003cState\u003e(StoreBuilderCallback\u003cState\u003e);\ntypedef StoreBuilderCallback\u003cState\u003e = Function(StoreBuilder\u003cState\u003e builder);\n\nabstract class StoreBuilder\u003cState\u003e {\n  StoreBuilder\u003cState\u003e withPreloadedState(State preloadedState);\n  StoreBuilder\u003cState\u003e withReducer(Reducer\u003cState\u003e reducer);\n  StoreBuilder\u003cState\u003e withMiddleware(Middleware\u003cState\u003e middleware);\n  StoreBuilder\u003cState\u003e usingDevtools(String devToolsIpAddr);\n}\n```\n\n### Quick walkthrough to get `redux_remote_devtools` to work\n\n1. Make sure you have the `remotedev-server` command available in your computer. If you have it, skip until step 2, otherwise, read-on. You have the option of installing it as a dockerized container or as an npm package, I'll show you how to do both:\n\n```bash\n# First: the npm installation\nnpm i -g remotedev-server\n# or\nyarn global add remotedev-server\n\n# Then launch it\nremotedev --port 8000\n\n# Second: the docker way\n# The following command will pull the image if you don't have it\n# and will leave the server running, no further setup required\ndocker run -p 8000:8000 jhen0409/remotedev-server\n```\n\n2. Use the `usingDevtools` method in your `StoreBuilder` to pass the IP address and port in which you're running your server.\n\n```dart\n// To use any IP address within your LAN\nbuilder.usingDevTools('192.168.1.2:5000');\n\n// Or if you want to use loopback\nbuilder.usingDevTools('127.0.0.1:5000');\nbuilder.usingDevTools('localhost:5000');\n```\n\n3. Make sure everything you want to see in your devtools is json serializable, this means, all your model classes and your state itself. If you want to see your actions properly with all their payloads and stuff they should be json serializable too. The recommended way to achieve this is via the `json_serializable` package, you can check out the example project for that. Basically, all you do is this:\n\n```dart\n// todo.dart\n\nimport 'package:json_annotation/json_annotation.dart';\n\n// specify the name of the file where the generated code will be\npart 'todo.g.dart';\n\n// annotate your class with @JsonSerializable() from json_annotation\n@JsonSerializable()\nclass Todo {\n  final int id;\n  final String title;\n  final bool completed;\n\n  const Todo({\n    this.id,\n    this.title,\n    this.completed,\n  });\n\n  // Use the generated code in the factory and in the toJson methods\n  factory Todo.fromJson(Map\u003cString, dynamic\u003e json) =\u003e _$TodoFromJson(json);\n  Map\u003cString, dynamic\u003e toJson() =\u003e _$TodoToJson(this);\n}\n```\n\n```bash\nflutter pub run build_runner build # or replace build for watch if you want the generated code to be automatically updated as you write more code :)\n```\n\n4. Run your app and see your redux store in real time. You'll also have time travel debugging available for you.\n\nIf I missed anything be sure to check out the official docs for [`redux_remote_devtools`](https://github.com/MichaelMarner/dart-redux-remote-devtools) and let me know or make a PR with the correction.\n\n## Reducers and Actions\n\n### `createReducer`\n\nHere is your alternative to writing reducers like the next one:\n\n```dart\nState reducer(State s, dynamic a) {\n  if (a is Action1) {\n    return sPrime;\n  }\n\n  if (a is Action2) {\n    return sSecond;\n  }\n\n  // Tons of if statements like the ones before\n\n  return s;\n}\n```\n\nThe previous reducer would have to be written like this:\n\n```dart\nfinal reducer = createReducer\u003cAppState\u003e(\n  AppState.initialState,\n  (builder) =\u003e builder\n    .addCase\u003cAction1\u003e((state, action) =\u003e sPrime)\n    .addCase\u003cAction2\u003e((state, action) =\u003e sSecond),\n);\n```\n\nIf you need to run some code everytime an action is dispatched that is an instance of `MyGenericAction\u003cT\u003e` regardless of what `T` is you'll have to use `addMatcher`.\n\n```dart\nfinal reducer = createReducer\u003cAppState\u003e(\n  AppState.initialState,\n  (builder) =\u003e builder\n    .addMatcher(\n      (action) =\u003e action.runtimeType.toString().startsWith('MyGenericAction'),\n      (state, action) =\u003e sPrime\n    ),\n);\n```\n\nLastly, if you need to change what your reducer does when it receives an action you didn't add a case or matcher for you can just add a default case using `addDefaultCase`.\n\n```dart\nfinal reducer = createReducer\u003cAppState\u003e(\n  AppState.initialState,\n  (builder) =\u003e builder\n    .addDefaultCase((state) =\u003e sPrime),\n);\n```\n\n#### Enum actions\n\nSince enum values are not types it is not possible to add a case for these but you can still use a matcher like the following:\n\n```dart\nenum Actions {\n  increment,\n  decrement\n}\n\nfinal reducer = createReducer\u003cint\u003e(0, (builder) {\n  builder\n      .addMatcher((action) =\u003e action == Actions.increment,\n          (state, action) =\u003e state + 1)\n      .addMatcher((action) =\u003e action == Actions.decrement,\n          (state, action) =\u003e state - 1);\n});\n```\n\n`Actions.increment` would be serialized as `{ \"type\": \"increment\" }` for `remote-devtools`.\n\n#### API Reference\n\n```dart\ntypedef ActionMatcher\u003cAction\u003e = bool Function(Action action);\ntypedef CaseReducer\u003cState, Action\u003e = State Function(State state, Action action);\ntypedef DefaultCaseReducer\u003cState\u003e = State Function(State state);\ntypedef BuilderCallback\u003cState\u003e = Function(ActionReducerMapBuilder\u003cState\u003e builder);\n\nabstract class ActionReducerMapBuilder\u003cState\u003e {\n  ActionReducerMapBuilder\u003cState\u003e addCase\u003cAction\u003e(\n      CaseReducer\u003cState, Action\u003e reducer);\n  ActionReducerMapBuilder\u003cState\u003e addMatcher\u003cAction\u003e(\n      ActionMatcher\u003cAction\u003e actionMatcher, CaseReducer\u003cState, Action\u003e reducer);\n  ActionReducerMapBuilder\u003cState\u003e addDefaultCase(\n      DefaultCaseReducer\u003cState\u003e reducer);\n}\n\nReducer\u003cState\u003e createReducer\u003cState\u003e(State initialState, BuilderCallback\u003cState\u003e builderCallback);\n```\n\n### `PayloadAction` abstract class\n\nSince in flutter we use a different class for each action and that's how we differentiate them there is no `createAction` function like there is in the original `redux-toolkit` for `js` but there is a `PayloadAction` interface for you to implement so that all your actions follow the same format.\n\n```dart\n@immutable\nclass MyAction extends PayloadAction\u003cPayload, Meta, Error\u003e {\n  const PayloadAction({\n    Payload payload,\n    Meta meta,\n    Error error,\n  }) : super(\n    payload: payload,\n    meta: meta,\n    error: error,\n  );\n}\n```\n\nAnother, simpler, example is this class I took from the example. It's the action I dispatch when I want to complete a TODO item when I tap on one:\n\n```dart\n@immutable\nclass CompleteTodo extends PayloadAction\u003cTodo, dynamic, dynamic\u003e {\n  const CompleteTodo(Todo todo) : super(payload: todo);\n}\n```\n\n### `AsyncThunk` abstract class\n\nAgain, no `createAsyncThunk` like in the original but an abstract class. This is an application of the template method design pattern so I'll allow you to specify your operation that returns a `Future` by overriding the `run` method and I'll take care of dispatching actions as the state of your `Future` evolves.\n\nThe next example shows a thunk that fetches a list of todos from the [JSON Placeholder API](https://jsonplaceholder.typicode.com) and transforms the json it receives into a model class.\n\n```dart\n@immutable\nclass FetchTodos extends AsyncThunk\u003cFetchTodos, AppState, void, List\u003cTodo\u003e\u003e {\n  @override\n  Future\u003cList\u003cTodo\u003e\u003e run() async {\n    final response = await http.get('${Config.apiBaseUrl}/todos');\n    final list = jsonDecode(response.body) as List\u003cdynamic\u003e;\n    return list.map((e) =\u003e Todo.fromJson(e)).toList();\n  }\n}\n```\n\n### Convenience exports\n\nJust like the original `redux-toolkit` I re-exported some useful functions and even entire libraries just for convenience.\n\n- `nanoid` -\u003e An inlined copy of nanoid/nonsecure. Generates a non-cryptographically-secure random ID string.\n- [`reselect`](https://github.com/brianegan/reselect_dart) -\u003e Everything that `reselect` exports I export for convenience.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrnkr%2Fredux_toolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmrnkr%2Fredux_toolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmrnkr%2Fredux_toolkit/lists"}