Git Product home page Git Product logo

redux.dart's Introduction

Build Status codecov Flutter Community: redux

Redux for Dart using generics for typed State. It includes a rich ecosystem of Docs, Middleware, Dev Tools and can be combined with Flutter using the flutter_redux package.

Docs

  • Motivation and Principles - Learn why Redux might make sense for your app and the principles behind it.
  • Basics - Introduction to the core concepts in Redux
  • Combining Reducers - combineReducers works a bit differently in Dart than it does in JS. Learn why, and how to combine reducers in a type-safe way!
  • Async with Middleware - Learn how to make async calls, such as to a web service or local database, with Redux.
  • Use Selectors to Query Data in the Store - Learn how to write functions to provide consistent access to data in your App State.
  • API Documentation - Rich documentation included in the source code and generated by DartDoc.

Flutter Examples

To integrate Redux.dart with Flutter, please use the flutter_redux package.

Beginner

Intermediate

Advanced

Middleware

  • redux_logging - Connects a Logger to a Store, and can print out actions as they're dispatched to your console.
  • redux_thunk - Allows you to dispatch functions that perform async work as actions.
  • redux_future - For handling Dart Futures that are dispatched as Actions.
  • redux_epics - Middleware that allows you to work with Dart Streams of Actions to perform async work.

Dev Tools

The redux_dev_tools library allows you to create a DevToolsStore during dev mode in place of a normal Redux Store.

This DevToolsStore will act exactly like a normal Store at first. However, it will also allow you to travel back and forth throughout the States of your app or recompute the State of your app by replaying all actions through your reducers. This works perfectly with Hot Reloading!

You can combine the DevToolsStore with your own UI to travel in time, or use one of the existing options for the platform you're working with:

Additional Utilities

  • reselect - Efficiently derive data from your Redux Store with memoized functions.
  • redux_persist - Persist Redux State, works for Web and Flutter

Usage

import 'package:redux/redux.dart';

// Create typed actions. You will dispatch these in order to
// update the state of your application.
enum Actions {
  increment,
  decrement,
}

// Create a Reducer. A reducer is a pure function that takes the 
// current State (int) and the Action that was dispatched. It should
// combine the two into a new state without mutating the state passed
// in! After the state is updated, the store will emit the update to 
// the `onChange` stream.
// 
// Because reducers are pure functions, they should not perform any 
// side-effects, such as making an HTTP request or logging messages
// to a console. For that, use Middleware.
int counterReducer(int state, action) {
  if (action == Actions.increment) {
    return state + 1;
  } else if (action == Actions.decrement) {
    return state - 1;
  }
  
  return state;
}

// A piece of middleware that will log all actions with a timestamp
// to your console!
// 
// Note, this is just an example of how to write your own Middleware.
// See the redux_logging package on pub for a pre-built logging 
// middleware.
loggingMiddleware(Store<int> store, action, NextDispatcher next) {
  print('${new DateTime.now()}: $action');

  next(action);
}

main() {
  // Create the store with our Reducer and Middleware
  final store = new Store<int>(
    counterReducer, 
    initialState: 0, 
    middleware: [loggingMiddleware],
  );

  // Render our State right away
  render(store.state);
  
  // Listen to store changes, and re-render when the state is updated
  store.onChange.listen(render);

  // Attach a click handler to a button. When clicked, the `INCREMENT` action
  // will be dispatched. It will then run through the reducer, updating the 
  // state.
  //
  // After the state changes, the html will be re-rendered by our `onChange`
  // listener above. 
  querySelector('#increment').onClick.listen((_) {
    store.dispatch(Actions.increment);
  });
}

render(int state) {
  querySelector('#value').innerHtml = '${state}';
}

redux.dart's People

Contributors

andrasferenczi avatar bcko avatar brianegan avatar caongocthai avatar cillianmyles avatar issmirnov avatar johnpryan avatar kevmoo avatar lukepighetti avatar maxlapides avatar md-weber avatar michaelmarner avatar sidrao2006 avatar stereotype441 avatar thekeenant avatar timothysteward-wk 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  avatar  avatar  avatar  avatar  avatar

redux.dart's Issues

Error: Method not found: 'TypedMiddleware'.

Hey,

I get the following error:

compiler message: lib/middleware/store_middleware.dart:48:9: Error: Method not found: 'TypedMiddleware'.
compiler message:     new TypedMiddleware<AppState, LoadSchoolsAction>(loadSchools),

My code looks like

List<Middleware<AppState>> defaultList = [
    new LoggingMiddleware.printer(),
    new TypedMiddleware<AppState, LoadSchoolsAction>(loadSchools),
]

Middleware<AppState> _createLoadSchools(SchoolRepository repository) {
  return (Store<AppState> store, action, NextDispatcher next) {
    if (action is LoadSchoolsAction) {
       
    }

    next(action);
  };
}

And the same happens with TypedReducer

compiler message: lib/on_boarding/on_boarding_reducer.dart:12:7: Error: Method not found: 'TypedReducer'.
compiler message:   new TypedReducer<OnBoardingState, SchoolsLoadedAction>(_setLoadedSchools),

Any idea?

Middleware creation failure does not throw error

For the following code:

List<Middleware<AppState>> createMiddlewares() {
  final onFetchWishListMiddleware = _onFetchWishList();

  return [
    TypedMiddleware<AppState, FetchWishListAction>(onFetchWishListMiddleware),
  ];
}

Middleware<AppState> _onFetchSomething(SomethingService service) {
  return (Store<AppState> store, action, NextDispatcher next) async* {
    next(action);

    var user = selectors.getCurrentUser(store.state);

    service.list().listen((List<Something> items) {
      store.dispatch(FetchSomethingSuccessAction(items));
    });
  };
}

I accidentally put in async* which shouldn't be there, then this action's middleware and reducer wouldn't run, but it also didn't throw any error. Are we able to provide Middleware with a better type so we get warnings on things like this early?

Filed an issue in flutter_redux but we thought it's better suited here (brianegan/flutter_redux#178)

IntellJ complains about typings when combining reducers

First of all, thank you for this libary !

So when I combine reducers in IntelliJ (AndroidStudio), this happnens:

This works fine:

final Reducer eventFilterReducer = combineReducers([
  addFilterReducer,

]);



List<ZEventFilter> addFilterReducer (eventFilters, action){
  return List.from(eventFilters)..add(action.eventFilter);
}

This lints a typecast error.

final Reducer eventFilterReducer = combineReducers([
  addFilterReducer <--"The element type '(List<ZEventFilter>, dynamic= -> List<ZEventFilter>' can't be assigned to the list type '(dynamic, dynamic) -> dynamic'" ,

]);



List<ZEventFilter> addFilterReducer (List<ZEventFilter> eventFilters, AddEventFilterAction action){
  return List.from(eventFilters)..add(action.eventFilter);
}

Must be the linter, not the libary right?

Best regards,
Daniel

Q: why doesn't store have a stream of dispatched actions?

Hi there,

I've just found myself wanting Store to expose Stream<dynamic> get actions and am wondering why it doesn't.

I'm trying to test push notification logic in my app as best I can without actually integrating with third party code. When push notifications are received (which happens in the widget layer), they are immediately dispatched in a "raw" state inside an action. The middleware then processes the raw data and dispatches either a failure action, or - depending on the data in the push notification - a more specific action with strongly-typed data. The failure actions just end up in the log - nothing more.

So I'm trying to write tests that send dodgy push notifications via a dispatch and then verifying that an appropriate failure action is dispatched. But without store.actions, this is a bit messy to achieve. I instead need my tests to have some level of control over the middleware set up for them, and this isn't great because until now my tests have been able to remain blissfully ignorant of that.

Anyway, just a question. Maybe there's a really good reason not to expose this, but I can't think of one.

Updating only selected fields of immutable

Coming from the JavaScript world, seeing something like this in a reducer is common:

return {
    ...state,
    foo: "bar"
};

This is basically creating a new object, but replacing (not setting) the foo field, making it "immutable" (doesn't change the original, just creates a new with the updated field). This is usually compiled down to use Object.assign when using Babel.

In Dart and redux.dart, when making a new state, I found no way to "copy" the old object and replace a value. The only way I found was to fully recreate the object, reassigning all the fields from the old. This quickly becomes a problem when scaling, because if you creating a new field, you must check and add every single reducer branch. By failing to do so, you'll have an empty property when running that branch/action.

Is there any solution for this problem?

Update README.md for Dart 2

Commands such as pub serve in the README do not work in Dart 2. Would be nice if the documentations was updated for Dart 2.

Best practices for stateful widgets

Hi there. I've been working with redux for the last couple of weeks and I've been really enjoying the process of planning, coding, scaling and deploing apps with much more control than before. There's one detail though which I haven't found any good source digging in on the following topic: what are the best practices to use redux when you surely can't have a stateless widget?

I agree that this question can be vague, but I don't have enough experience in the field to make it specific for several development cases.

Is there any particular or general device for these situations?

Thanks!

Exposing a subset of the Store API to middleware

This is primarily a question about a difference between the middleware API here versus the JS version.

redux.js exposes only a subset of the Store's API (getState() and dispatch()) to middleware, whereas redux.dart provides the entire Store instance. Are there any valid use cases where having access to reducer, onChange, or teardown justifies their presence?

It seems to me that using those additional Store APIs in middleware would usually be considered anti-patterns and limiting middleware to just the Store's state and dispatcher would be a useful constraint and would make test setups a bit simpler (although this might be a marginal improvement, at best).

Obviously this would be a breaking API change, and the benefit of preventing misuse in middleware and aligning with redux.js is relatively small, so I definitely understand if you decide the juice isn't worth the squeeze. Maybe something to consider in the future if you have other reasons for a new major version.

Change the return type of `Store.dispatch` to `void`

I've noticed that Store.dispatch method doesn't return anything. Never. So it would be a good idea to specify it

NextDispatcher typedef also should return void, not a dynamic

I can create a pull request with this change, if you agree with me

Dart 2 issue

Hello,

after updating to dart 2. I get the following error when using Reducer

error is happend type '(OnBoardingState, LicensesLoadedAction) => OnBoardingState' is not a subtype of type '(OnBoardingState, dynamic) => OnBoardingState'

My code looks like the following:

final onBoardingReducer = combineTypedReducers<OnBoardingState>([
  new ReducerBinding <OnBoardingState, LicensesLoadedAction>(_setLoadedLicenses),
]);

OnBoardingState _setLoadedLicenses(OnBoardingState onBoardingData, LicensesLoadedAction action) {
  return onBoardingData.copyWith(licenses: action.licenses);
}

If I set the type to dynamic it works but I would prefer to not have to case the action.

OnBoardingState _setLoadedSchools(OnBoardingState onBoardingData, dynamic action) {
  if(action is SchoolsLoadedAction) {
    return onBoardingData.copyWith(schools: action.schools);
  }
  return onBoardingData;
}

Any idea?

Project description?

Someone asked me today if we could add a description to the Github repo, which is unfortunately one thing I don't have access to. Could we add please one?

Could be as simple as the "Redux for Dart" description we have in the pubspec.

Exception AppState' is not a subtype of type 'Reducer<AppState>' when creating a store.

Hi, after creating a simple application I get following exception:

type '(AppState, dynamic) => AppState' is not a subtype of type 'Reducer' of 'reducer'.

Here's my setup:

dependencies:
  flutter_redux: "^0.5.0"
  redux: "^3.0.0"

@immutable
class AppState {
  final List<double> history;
  final bool screen;

  AppState(this.history, this.screen) {}

  factory AppState.initial() => new AppState(new List.unmodifiable([]), false);
}

AppState appReducer(AppState state, action) {
  return new AppState.initial();
}

final store = new Store<AppState>(
		appReducer,
		initialState: new AppState.initial(),
	);

If I change 'AppState' to 'int' in the reducer, the app runs OK.
Is there something wrong with my code?

Cheers,
Artur

design problem

When Flutter want to rebuild the dom,we need to setState and call _element.markNeedsBuild();
I want to know the redux how to rebuild the dom, is it also call setState or just full rendering the dom.

ReduxInit action

Store doesn't dispatch init action once initialized. Both swift and kotlin redux libs do it.

This action is important to setup app long running services on app startup.

The argument type '(#lib1::AppState, dynamic) → #lib1::AppState' can't be assigned to the parameter type '(#lib2::AppState, dynamic) → #lib2::AppState'.

I get the above compiler error when building my flutter app, the error relates to the appReducer parameter in the Store instantiation in main.dart. I've searched and found people having similar issues, but in their case it was always caused by relative paths which I am not using.

I've gone through my code and the docs a number of times and I can't see anything wrong so I think it may be a bug but please correct me if I am missing something.

This may be an issue with flutter_redux rather than redux.dart so I've opened an issue there also.

-main.dart-
class MainApp extends StatelessWidget {
  final store = Store<AppState>(
    appReducer, 
    initialState: new AppState(),
    middleware: []
      ..addAll(createAuthMiddleware())
      ..add(new LoggingMiddleware.printer()),
  );
.
.
.
-app_state.dart-
class AppState {
  final User currentUser;

  AppState({
    this.currentUser,
  });

  AppState copyWith({User currentUser}) {
    return new AppState(
      currentUser: currentUser ?? this.currentUser,
    );
  }

  @override
  String toString() {
    return 'AppState{currentUser: $currentUser}';
  }
}
-auth_actions.dart-
class LogIn {}

class LogInSuccessful {
  final User user;

  LogInSuccessful({ @required this.user});

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

class LogInFail {
  final dynamic error;
  LogInFail(this.error);
  @override
  String toString() {
    return 'LogIn{There was an error logging in: $error}';
  }
}

class LogOut {}

class LogOutSuccessful {
  LogOutSuccessful();
  @override
  String toString() {
    return 'LogOut{user: null}';
  }
}

-app_reducer.dart-
AppState appReducer(AppState state, dynamic action) {
  return new AppState(
    currentUser: authReducer(state.currentUser, action)
  );
}
-auth_reducer.dart-
final authReducer = combineReducers<User>([
  TypedReducer<User, LogInSuccessful>(_logIn),
  TypedReducer<User, LogOut>(_logOut),
]);

User _logIn(User user, dynamic action) {
  return action.user;
}

Null _logOut(User user, dynamic action) {
  return null;
}
-pubspec.yaml-
environment:
  sdk: ">=2.0.0-dev.68.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  redux: ^3.0.0
  flutter_redux: ^0.5.2
flutter --version
Flutter 0.8.2 • channel beta • https://github.com/flutter/flutter.git
Framework • revision 5ab9e70727 (3 weeks ago) • 2018-09-07 12:33:05 -0700
Engine • revision 58a1894a1c
Tools • Dart 2.1.0-dev.3.1.flutter-760a9690c2

TypedReducer: A value of type '(dart.core::bool, dynamic) → dart.core::bool' can't be assigned to a variable of type '(dynamic, dynamic) → dynamic'

After upgrading my project to flutter-redux: 0.5.0 and dart-redux: 3.0.0 I’m having this issue.
The code rising the error is:

final hasInternetReducer = combineReducers([
  TypedReducer<bool, LostInternetAction>((bool state, LostInternetAction action) => false),
  TypedReducer<bool, RecoveredInternetAction>((bool state, RecoveredInternetAction action) => true),
]);

The complete error is:

lib/redux/reducer.dart:83:7: Error: A value of type '(dart.core::bool, dynamic) → dart.core::bool' can't be assigned to a variable of type '(dynamic, dynamic) → dynamic'.
Try changing the type of the left hand side, or casting the right hand side to '(dynamic, dynamic) → dynamic'.

I'm using dart 2 with flutter v0.2.7-pre.1
No issues found by flutter doctor

Proposal - have dispatch return original action

I am trying to port some promise based code from node and with redux thunks it was easy to return a promise from the action. What would be helpful would be if the dispatch method on the store returned the action then we could do something like:

With a LoginAction object add a Completer property and also a loginCompleted method that returns a future from the Completer. We intercept the LoginAction in the middleware and do some async stuff then call the completer to complete the action. This would keep the async flow in for example a button handler located in the button handler and allow integration with navigators and the ui in a predictable and easy manner.

class Action {
final completer = new Completer();

loginCompleted() { return completer.future; }
}

store.dispatch(action).loginCompleted().then((result) => print('logged in state $result');

The middleware will take the action and do some async stuff. when the middleware async operation completed the completer.complete will be called and the future completes.

Creating an simple example

A simple example to help many guys approach redux.

  • Reducers tests
  • Thunk middleware tests
  • Injecting repository
  • Using built_value and built_collection for:
    • are immutable, if the elements/keys/values used are immutable;
    • are comparable;
    • are hashable;
    • use copy-on-write to avoid copying unnecessarily.

I will take care of this task.

TypedReducer is not extendable

Current TypedReducer looks:

class TypedReducer<State, Action> implements ReducerClass<State> {
  /// A [Reducer] function that only accepts an action of a specific type
  final State Function(State state, Action action) reducer;

  /// Creates a reducer that will only be executed if the dispatched action
  /// matches the [Action] type.
  TypedReducer(this.reducer);

  @override
  State call(State state, dynamic action) {
    if (action is Action) {
      return reducer(state, action);
    }

    return state;
  }
}

I prefer to extend TypedReducer instead pass function. It looks more convenient and by this reason I am using next version of TypedReducer:

abstract class TypedReducer<State, Action> implements ReducerClass<State> {
  factory TypedReducer(State Function(State state, Action action) reducer) =>
      _TypedReducer<State, Action>(reducer);

  TypedReducer.default();

  @override
  State call(State state, dynamic action) {
    if (action is Action) {
      return reduce(state, action);
    }

    return state;
  }

  State reduce(State state, Action action);
}

class _TypedReducer<State, Action> extends TypedReducer<State, Action> {
  final State Function(State state, Action action) reducer;

  _TypedReducer(this.reducer) : super.default();

  @override
  State reduce(State state, Action action) => reducer(state, action);
}

It doesn't make any conflict with current code and I think this version can be helpful for other users of this library.

Could you provide an example for angular?

Hi!
Would it be possible to provide an example for Angular? For example showing how to instantiate and inject a store service, comparable to storeConnector for Flutter?
Thanks!

Travis.ci support

I've written a travis.yml file so we can verify the tests pass on PRs, but I don't think I can add this project to travis-ci.org. Would you be willing to connect this project to travis-ci?

Store always trigger even state does not change

https://github.com/johnpryan/redux.dart/blob/master/lib/src/store.dart#L197-L206

  // The base [NextDispatcher].
  //
  // This will be called after all other middleware provided by the user have
  // been run. Its job is simple: Run the current state through the reducer,
  // save the result, and notify any subscribers.
  _reduceAndNotify(dynamic action) {
    final state = reducer(_state, action);
    _state = state;
    _changeController.add(state);
  }

Issues

  1. Changes are always triggered even if the state didn't change
  2. The internal state is updated before the stream update is received so validating state == newState is equivalent to using if (1 == 1)

Possible Solutions

I'd like to get your thoughts on these two.

Solution 1
Keep this as-is, but see swap the order so in our onChange handler we can properly compare our states.

Solution 2
Remove some of the blackbox so we can extend Store with our own implementation to adjust it accordingly to our desired flavor.

Reasoning

We use built_value for our state. This means any state change will always return a guaranteed new object or the exact same object due to the reducer mutating the immutable.

Through immutability we're able to better control React render cycles which greatly improves performance. With a store always triggering, we have to do internal state checks – where we validate each individual value – negating the inherent equality checks you get from using built_value and, technically, "duplicating" generated code.

Thoughts?

Avoiding merge headaches

Hi,

I'm fairly new to redux, and am trying to define an architecture that will allow different developers working on independent features to avoid stepping on each other's toes. Certainly, composing reducers helps in this regard, and the docs do a great job of explaining how to achieve that.

However, I don't think composing reducers is going to be enough to avoid merge conflict headaches. If feature A and feature B are worked on in tandem, that will require:

  1. New AppState.AState and AppState.BState fields
  2. New reducers for AState and BState
  3. New middleware to handle asynchronous operations (for example) on behalf of both AState and BState
  4. AppState.hashCode be updated
  5. AppState.operator == be updated
  6. AppState.toString be updated

Composing reducers helps with point 2, but not the other points. Is there any guidance on avoiding conflicts and merge pain outside of reducers?

To be honest, I went looking for some kind of "composite state" support in redux, so you could do something like:

class AppState extends CompositeState {
  final List<ChildState> childState;

  AppState(List<ChildState> childState);
}

class FeatureA extends ChildState {};

class FeatureB extends ChildState {};

I couldn't find anything, so I tried spiking something, but it gets messy quite quickly. For the record (and for my future reference, as I'm about to can it) I ended up with:

@immutable
class AppState {
  final List<ChildStore<dynamic>> childStores;

  AppState.initial()
      : childStores = <ChildStore<dynamic>>[
          new LoginStore.initial(),
        ];

  AppState._withChildStores(this.childStores) : assert(childStores != null);

  AppState copyWith({List<ChildStore<dynamic>> childStores}) =>
      new AppState._withChildStores(childStores ?? this.childStores);

  State getChildState<State>() {
    for (var childStore in childStores) {
      if (childStore is ChildStore<State>) {
        return childStore.state;
      }
    }

    assert(false, "Failed to find child store of type ChildStore<$State>.");
    return null;
  }

  @override
  int get hashCode {
    var hash = 0;

    for (var childStore in childStores) {
      hash ^= childStore.hashCode;
    }

    return hash;
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      (other is AppState && listsEqual(childStores, other.childStores));

  @override
  String toString() => "AppState{childStores: $childStores}";
}

abstract class ChildStore<State> {
  State get state;

  ChildStore<State> withState(State state);

  bool canHandle(dynamic action);

  Reducer<State> getReducer();

  List<Middleware<State>> getMiddleware();
}

I gave up when I got the middleware stage because things are just getting too convoluted.

But you can see that such a mechanism would allow developers to work on independent features with only a single point of merge contention: the creation of the initial AppState. Any conflicts here would be trivial to resolve.

Improper fat arrow in examples

In the TypedReducer example it shows a bunch of fat arrows with the word return after it. I’m fairly new to dart but I think the fat arrow explicitly returns. When I tried to use the return statement following the example it was showing an error

Use optional type instead of dynamic for actions

Proposal

It'd be great if Store had another generic type to be used as the action argument type in methods like dispatch, preventing non-action objects from being used as actions.

This should be non-breaking for most users, as type inference should allow Store constructors to omit the existing state type, and the addition of another type will only break invocations that specify it explicitly.

Reasoning

I've separated my project into layers, like so:

Data:
project_data: Platform-agnostic repository implementations

Domain:
project_domain: Core platform-agnostic functionality, including state management

Platform:
project_flutter: A GUI using Flutter
project_cli: A CLI

Platform implementations can build a Redux store with a buildStore function in the domain package, passing in repository implementations to use. The domain package exports different action classes that the platform implementation can dispatch to the store. Each action class extends from an internal (non-exported) AppAction class.

If the dispatch function only accepts AppAction subtypes, this will prevent non-action objects being used by the platform implementations, requiring all actions to be used from the domain package.

Workarounds

At the moment, an assert can be used, but this throws an error at runtime. A compilation error is preferred.

AppState appReducer(AppState state, dynamic action) {
  assert(action is AppAction,
      'The action object ($action) has an invalid type (${action.runtimeType}! It must extend AppAction.');

  // ...
}

Best way to open a dialog

I'm wondering what is the best way to open a dialog?

My scenario:

In the middleware, I'm doing some async backend tasks. Depending on the result, I want to open a dialog to display an error message or to indicate that the user needs to log in again.

When the async task completes, I send another action to change the status, such as showErrorDialog = true.
In the UI code, I listen for this status (using https://github.com/brianegan/reselect_dart) and when it becomes true, I open the dialog and set this status to false (so it will not be reopened).

It works, but it also looks wrong to me.

Is there a better way to open a dialog only when an action is triggered?

Feedback regarding quickly getting redux up & running

After the dartconf talk, I decided to get redux running.

I quickly found package:redux, and had the talk quickly in my memory, so I was surprised to hit a few little problems:

  • It took me a while to realize that I needed to also use package:flutter_redux. This could be worth putting in bold early on in the readme: "for use with flutter, use package:flutter_redux which includes this package".
  • Documentation for how redux works, and how flutter_redux works, are kind of fragmented. It makes sense that the code should be fragmented, but the docs need not be.
  • The only available examples are either the counter app (which doesn't even really have any business using redux), or the todo app (which is architected to be absolutely perfect everywhere, making it a real leap to jump into).

As such, I'd recommend making a single example where

  • the state is a class (not an int)
  • the actions are objects (not an enum)
  • the store is passed along via StoreProvider
  • the app has multiple routes
  • those routes have connections to the store via StoreBuilder and StoreConnector

There are other things that might be worth covering for other people:

  • DIY "reselectors"
  • true reselectors with the reselect package
  • adding the dev tools to the ui
  • recommended directory structures for these things

But ultimately, I would recommend as little as possible! Its worth noting, the main examlpe on flutter_redux.dart is really really close to my request :)

Maybe this all exists in a blog post....I tend to search through docs first enough and at a certain completeness threshold (which this project crosses, great job!!) I don't look for blog posts as guides. If the docs are not intended to be as useful as any well-tailored, existing blogposts out there, it would be useful for the docs to link to some of these blogposts.

Speaking of blog posts, I think a lot of the documentation covers "why?" which would probably be better as an external link: "For reasons why flutter developers should consider using redux, consider reading X, Y, Z, Q, articles"

[feature ask] getter to ask if the store has been torn down

HI 👋 Redux Team,

I've got a redux store that exists briefly and sometimes it can be disposed of while a long running middleware server call is in flight. If I try to dispatch another action to set store state following that async call, there's an exception thrown because the store's change stream has been closed. To prevent those exceptions caused by calling dispatch on a torn-down store, it would be nice to be able to ask the store if it's still open to change.

Right now I have the following extension for this, but it requires getting tricky with the onChange stream.

extension StoreSafeAsyncDispatch on Store {
  /// if a store is closed it can not dispatch actions or apply changes
  Future<bool> get isClosed async {
    var closed = false;
    await onChange.listen((_) {}, onDone: () => closed = true).cancel();
    return closed;
  }

  /// only dispatch if the store is not closed
  Future<dynamic> asyncDispatch(dynamic action) async {
    if (await isClosed) return;
    return dispatch(action);
  }
}

Thanks for your time and consideration!

TypedMiddleware not accept specified middleware type.

Problem

When specify the exact action type, TypedMiddleware complains [dart] The return type '(Store<AppState>, LoginAction, (dynamic) → void) → Null' isn't a '(Store<AppState>, dynamic, (dynamic) → void) → void', as defined by the method 'loginMiddleware'..

Code

List<Middleware<AppState>> createMiddlewares({
  AccountService accountService,
}) {
  accountService = accountService ?? AccountService();

  return [
    TypedMiddleware<AppState, LoginAction>(loginMiddleware(accountService)),
  ];
}

Middleware<AppState> loginMiddleware(AccountService accountService) =>
    // Specify the exact type of action, not dynamic
    (Store<AppState> store, LoginAction action, NextDispatcher next) {
      next(action);
    };

Env

name: tangbole
description: A tumblr client app.

dependencies:
  cupertino_icons: ^0.1.2
  english_words: ^3.1.0
  flutter:
    sdk: flutter
  flutter_redux: ^0.5.1
  http: ^0.11.3+16
  meta: ^1.1.5
  uri: ^0.11.2
  redux: ^3.0.0
  redux_logging: ^0.3.0
  redux_persist: ^0.7.0-rc.2
  redux_persist_flutter: ^0.6.0-rc.1

dev_dependencies:
  flutter_driver:
    sdk: flutter
  flutter_test:
    sdk: flutter
  mockito: ^2.2.3

flutter:
  uses-material-design: true
  assets:
    - images/lake.jpg

Return type in the TypedMiddleware is void

The middleware has the return type void in the TypedMiddleware class

class TypedMiddleware<State, Action> implements MiddlewareClass<State> {
/// A [Middleware] function that only works on actions of a specific type.
final void Function(
Store<State> store,
Action action,
NextDispatcher next,
) middleware;
/// Create a [Middleware] that is only executed when the dispatched action
/// matches the [Action] type.
TypedMiddleware(this.middleware);
@override
dynamic call(Store<State> store, dynamic action, NextDispatcher next) {
if (action is Action) {
return middleware(store, action, next);
} else {
return next(action);
}
}
}

I've expected that after merging #52 middleware should have the return type dynamic, but this line was not changed. Is it a bug or is there a reason for this behavior?
Possible pull request: #66

CombineReducers should only pass the current reducer's state slice to it

Currently combineReducers passes the whole app state to each reducer, this adds some overhead where every reducer implementation needs to be aware of the whole app state and return the values of the other reducers as well as the slice of state that it cares about.

For example counterReducer here has to return state.clickCount although it only cares about state.count

AppState counterReducer(AppState state, action) {
  switch (action) {
    case AppAction.increment:
      return new AppState(state.count + 1, state.clickCount);

Similarly clickCounterReducer needs to return state.count every time though it should really only be handling state.clickCount:

AppState clickCounterReducer(AppState state, action) {
  switch (action) {
    case AppAction.increment:
      return new AppState(state.count, state.clickCount + 1);

Whereas if it were to implement combineReducers the way reduxjs does then each reducer would only have to know about it's own slice of state, and can be ignorant of the whole AppState. That would mean counterReducer could look more like this:

int counterReducer(int state, action) {
  switch(action) {
    case AppAction.increment:
      return state.count + 1;
  ...
}

This is probably easier to implement in js since the whole state is basically a map, and each slice of state is tied to a key on the map, which also corresponds to the reducer - being unfamiliar with dart I'm not too sure about how this would be achieved while also allowing custom classes for the state object.

Split the package into more files

The package consists in basically two files, which I guess it's okay because it's simple. However, I think if it was splitted it would reveal more "intent", make it more clean.

Problem with package:todo/redux/actions.dart

I want to do my first redux app( redux:3.0.0 & MacOs high Sierra) but I have the next error:

Your application could not be compiled, because its dependencies could not be established.
The following Dart file:
/Users/jonathan/.pub-cache/hosted/pub.dartlang.org/redux-3.0.0/lib/src/store.dart
...refers, in an import, to the following library:
package:todo/redux/actions.dart
That library is in a package that is not known. Maybe you forgot to mention it in your pubspec.yaml file?

Some idea?

async middleware and reducers - start, success, fail - doesn't seem to achieve its purpose

After we start a fetch action: store.dispatch(FetchCustomerStartAction());
We expect the reducer for this action to kickstart immediately to change inProgress to true, while middleware is awaiting response from server. However, the middleware pattern will not pass control to the reducer (next(Action)) until server call returns in the middleware (await completes). This means the transition of state.inProgress is not useful. (Apologies if its because I've gotten it all wired wrongly)

Creating a store with multiple reducers

I'm curious as to how one would go about creating a store with multiple reducers when using the combineReducers method when calling new Store?

For instance, if I wanted to pass the entire store down at a top level & have the store available anywhere in my app with all of the reducers.

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.