netopyr / reduxfx

Functional Reactive Programming (FRP) for JavaFX

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

API of Middleware

manuel-mauky opened this issue · comments

Hi,
I'm having problems understanding the API of the middleware in reduxfx.

In the original redux API a middleware "is a higher-order function that composes a dispatch function to return a new dispatch function." redux docs.
Additionally the constructor of a middleware has also access to the getState function.
Also see the detailed API description of applyMiddleware.

In reduxfx on the other hand the middleware operates with the Reducer/Updater function. It takes a reducer as argument and returns an enhanced reducer.

If I like to implement, for example, a thunk-middleware, I need access to the getState method because Thunk-Actions have to be able to decide which async actions to dispatch based on the current state. As far as I can see this isn't possible with the current reduxfx middleware API?

In my opinion it would be a good idea to stay as close to the original redux API as possible. But maybe I've overlooked something? Can you describe your ideas and your reasoning behind the current API?

At the moment I'm playing around with the reduxfx code to get this working but it takes some time because I'm not familiar with the reactivex API (yet).

Despite the name, ReduxFX is not an attempt to simply reimplement Redux for JavaFX. (Well, maybe it started that way...) :) I have looked at several frameworks that enable FRP for UI-development (mostly Elm, Cycle.js, and Redux) while designing ReduxFX, therefore it has become a mixture of different approaches. I.e. there is no original API that one can stay close to or not.

Redux does a lot things right, but the concept of thunks is one of the few I do not like. I did not plan to implement them and I am afraid I did not even consider them while designing ReduxFX. I am not really opposed to the idea of supporting them in ReduxFX and I can see their value when used in small applications with only few concurrent tasks.

At this point ReduxFX does not really have a store. Instead the main functionality is implemented through a couple of reactive streams. What you already can do is to listen to state-changes by subscribing to the Publisher that is returned from ReduxFX.getStatePublisher(). I guess all we need is a small adapter that subscribes to this publisher and caches the last value.

I wonder if this adapter should be integrated into the class ReduxFX or if this is something the thunk-middleware should do. So far the API of the ReduxFX class is mostly based on reactive streams. The adapter implements a paradigm shift, therefore I am reluctant to add it to the class. On the other hand, if this functionality is needed in several places, it would make sense to have it in a central place. Hmmm...

My suggestion is, that we leave it out of the ReduxFX class for now and wait to see how often it is needed. Instead we can add a separate component, that implements the adapter. What do you think?

Yes, I think my confusion comes from the name of the library. I'm totally ok with not reimplementing redux but using inspiration from other good libraries. However, then the name is indeed a little misleading, especially when terms like "middleware" are used but don't work like the "original redux" variants. On the other hand the term "middleware" is not redux specific but is used in many different contects so I think it's ok.

It's not my goal to have a thunk-middleware. I don't really "need" a thunk-middleware. I'm ok using another mechanism that allows me to create side-effects in a controlled way. If I understand your code and docs correctly this is done with the "Drivers"? I've seen the HttpDriver but I wonder how to do arbitrary side-effects other then http requests? Do I have to implement my own driver for this?

However, I think a good extension mechanism would be cool for the library. Thunk-middleware is not the only interesting middleware. For me it's not important that it works the same way with the same API as redux.js but it would be cool to have similar possibilities.
And Redux.js provides another even more powerful extension mechanism: Store Enhancers. It basically allows you to completely enhance/replace the reducer, the dispatch and getStore methods. The most common user of this is the redux-dev-tools but there are also libraries that for example persist and reload the redux-state in the local-storage.

My starting point was to implement a Redux-Devtool for JavaFX (which is also the reason for my recent PullRequests ;-) ). However, I had a hard time to get this working with the Reactive-Streams implementation of ReduxFX. So my approach for now is the same as with the fxml: Use a simple redux-clone for the first prototype to see what API is needed and then port it back to ReduxFX.

I'd love to be able to use a Redux-Devtool for JavaFX. It should not be too hard IMO, at least when it comes to the core functionality. Luckily most of the complex stuff is already solved in RxJava. What would we need to implement a Devtool for ReduxFX? I'd by happy to add the required hooks.

I have a first prototype here: https://github.com/lestard/redux-javafx-devtool
At the moment it shows dispatched actions and the current state in a treeview.
To integrate the redux-java lib I needed to intercept the dispatch and getState methods.

I need to be notified when an action is dispatched and when a new state is produced. I'm trying out how to do this with reduxfx-store now. The state view already works but I don't know how to get notified when actions are dispatched.

The next step is to find out how to implement time-traveling. For this most likely we need other interactions with the redux-library like replacing the state.

I've updated the devtool. It now also supports time-traveling. You can try it out with this example application.
However, it still only supports redux-java/jvm-redux-api at the moment.
I would love to support reduxfx too but I need help to get this to work.

I've introduced an interface that defines the connection between the redux-library and the dev-tool. We would need to create an implementation of this interface for reduxfx. I've also prepared the class for this here.
Feedback for this interface is highly welcome. If we need to adjust this to support reduxfx I'm happy to do so.
I've also prepared an example app for testing here.

I have to admit I am stuck here, because I am still struggling to understand the original design of Redux. I guess the problem boils down to this:

The dispatching mechanism is defined as an asynchronous feature. Or to use the official definition:

A dispatching function (or simply dispatch function) is a function that accepts an action or an async action; it then may or may not dispatch one or more actions to the store.

But a lot of code (including for example the redux-logger, but also the JvmReduxDevToolConnector) treat dispatch as if it was a synchronous call. From JvmReduxDevToolConnector:

final Object result = store.dispatch(action);
final STATE newState = store.getState();

My question is, how can you be sure that newState contains the state after the action was dispatched, if the store "may or may not dispatch one or more actions"? What am I missing?

In redux by default the dispatching is synchronous (see here for reference). Only if you add a middleware that "understands" some sort of asynchronous actions it can become asynchronous.
For example the Thunk-Middleware understands when Functions are dispatched. But in the end, the only way to change the state of the store is to invoke the normal synchronous dispatch method of the store.
Thunk allows you to invoke this synchronous dispatch method at a later point in time.

Middlewares compose/wrap the original dispatch method but in the end the original dispatch method has be invoked.

The code you mentioned is from the original dispatch method of the store. When this code runs I can't know if there there is still another asynchronous operation running. But it doesn't matter because I know that this operation has to use the original dispatch method to get it's result to the store and then I can intercept it.

Interesting is this sentence in the redux docs: "When the last middleware in the chain dispatches an action, it has to be a plain object. This is when the synchronous Redux data flow takes place."

I have created a pull request with a connector for ReduxFX.

To my own surprise no changes in ReduxFX were required. But I got some ideas how the ReduxFX-store could be made more extensible, which would make it easier to integrate tools like the devtool.