Git Product home page Git Product logo

reframe-middleware's Introduction

reframe-middleware: the ‘action first’ approach to redux

Reframe-middleware makes actions first class in redux.dart.

pub package

Inspired by Clojurescript's re-frame.

Flutter demo.

How to use

1. Add reframe_middleware to your pubspec.yaml:

dependencies:
  reframe_middleware: ^1.0.0

2. Add reframeReducer and reframeMiddleware to your redux.dart Store:

import 'package:reframe_middleware';

final store = Store<AppState>(
	reframeReducer, // produces new state
    initialState: AppState(), 
    middleware: [reframeMiddleware(), // handles actions
	             thirdPartyMiddleware, ...]);

3. Define an action:

Synchronous, pure action:

import 'package:reframe_middleware';

@immutable
class IncrementAction extends ReframeAction {
  @override
  ReframeResponse<AppState> handle(AppState state) =>
      ReframeResponse.stateUpdate(
        state.copy(count: state.count + 1));
}

Asynchronous, impure action (side-effect):

import 'package:reframe_middleware';

@immutable
class AsyncIncrementAction extends ReframeAction {
  @override
  ReframeResponse<AppState> handle(AppState state) =>
      ReframeResponse.sideEffect(() =>
          Future.delayed(Duration(milliseconds: 1000))
              .then((_) => [IncrementEvent()]));
}

An action that does both:

@immutable
class DoubleIncrementAction extends ReframeAction {
  @override
  ReframeResponse<AppState> handle(AppState state, Effects effects) {
    return ReframeResponse(
        nextState: Optional.of(state.copy(count: state.count + 1)),
        effect: () => Future.delayed(Duration(milliseconds: 1000))
            .then((_) => [IncrementAction()]));
  }

4. Dispatch... and done.

store.dispatch(IncrementAction());

How it works

Actions are handled by their own handle method:

action.handle(store.state) -> ReframeResponse

A ReframeResponse contains a new state and side effect.

@immutable
class ReframeResponse<S> {
  final Optional<S> nextState;
  final SideEffect effect;

  const ReframeResponse({
    this.nextState = const Optional.absent(),
    this.effect = noEffect,
  });
  
// A side-effect is a closure that becomes a list of actions
typedef SideEffect = Future<List<Action>> Function();
Future<List<Action>> noEffect() async => [];

For state updates, reframeMiddleware dispatches a special action StateUpdate to carry the new state to the reframeReducer.

For side-effects, reframeMiddleware runs the Future and dispatches the resulting actions.

// middleware
Middleware<S> reframeMiddleware<S, E>(E effects) =>
    (Store<S> store, dynamic event, NextDispatcher next) {
      if (event is ReframeAction) {
        event.handle(store.state, effects)
            // sends new state to reducer via StateUpdate action
          ..nextState
              .ifPresent((newState) => store.dispatch(StateUpdate(newState)))
           // runs side effects and dispatch resulting actions:
          ..effect().then((events) => events.forEach(store.dispatch));
      }

      // passes (1) the event to next middleware (e.g. 3rd party middleware)
      // and (2) a StateUpdate to the reducer
      next(event);
    };


// reducer
S reframeReducer<S>(S state, dynamic event) =>
    event is StateUpdate ? event.state : state;

FAQ

Do I need thunkMiddleware?

No. Reframe-middleware already does async logic -- that’s what ReframeResponse's effect is for.

Does this replace redux.dart or flutter-redux?

No. Reframe-middleware is supposed to be used with redux.dart (in the same way e.g. Flutter redux_persist is).

Reframe-middleware, like redux.dart, can be used with or without the excellent flutter-redux.

Doesn't this couple reducers and actions, which is discouraged?

Short answer: Yes.

Long answer:

There have been objections to 1:1 mappings between actions and reducers. (“The whole point of Flux/Redux is to decouple actions and reducers”).

But the decoupling of actions and reducers is an implementation detail of redux.js.

In contrast, Clojurescript re-frame intentionally couples an event (action) with its handler (reducer + middleware). Why?

Every redux system* -- Elm, re-frame, redux.js, redux-dart etc. -- is characterized by two fundamental principles:

  1. UI is explained by state ("state causes UI")
  2. state is explained by actions ("actions cause state")

When we dispatch an action we ask, "What does this action mean, what updates or side-effects will it cause?"

If you need to reuse state modification logic, reuse a function -- don't reuse a reducer.

*(In contrast, SwiftUI has 1 but not 2, and so is not a redux sytem.)

reframe-middleware's People

Contributors

pianostringquartet avatar

Watchers

James Cloos avatar  avatar

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.