Redux with JavaFX - RainFocus...Debugging with Redux - whole application state at one specific...

Post on 22-May-2020

8 views 0 download

transcript

Redux with JavaFXMichael Heinrichs & Manuel Mauky

Manuel MaukyMichael Heinrichs

Functional Reactive Programming

● Declarative● Immutable data

● No side effects● Avoid state changes

Functional Programming

Active Communication

Button Controllercalls

Controllerlisten

Button Controllerfireevent

notify

Reactive Communication

Reactive Hell

Component Component

Component

Component

Component

Component

Component

Component

Reactive Streams

Publisher ProcessorEvent

ProcessorEvent

SubscriberEvent

PublisherEvent

Subscriber

Event

Functional Reactive Programming

Publisher ProcessorEvent

ProcessorEvent

SubscriberEvent

immutable

“dirty” “dirty”pure

Overview

Application

Action

StateVirtualSceneGraph

Frame-work

Action Creator

View Creator

Reducer

Event

Redux with Java

Demo

Actions

Application

Action

StateVirtualSceneGraph

Frame-work

Action Creator

View Creator

Reducer

Event

Action

Frame-work

Action CreatorEvent

MouseEventKeyEventActionEventChangeEvent

class SetFilterAction { Filter value;}

Demo

State

Action

State

Frame-work

Action Creator

Reducer

Event

state

newTodo filter

todos

todo1 todo2 ...

class State { final String newTodo; final Filter filter; final Vector<Item> todos;}

class Item { final int id; final String text; final boolean completed;}

Demo

Reducer

Action

State

Reducer

State calcNewState(State currentState, Action action)

state_1 = calcNewState(state_0, action_0)

state_2 = calcNewState(state_1, action_1)

...

state_0

Initial state

State calcNewState(State currentState, Action action)

state_0

state_1 = calcNewState(state_0, action_0)

state_2 = calcNewState(state_1, action_1)

...

Stream actions = Stream.of(action_0, ..., action_n)

State state_n = actions.reduce(state_0, calcNewState)

Demo

State calcNewState(State state, Action action) {

if (action instanceof SetFilterAction) {

SetFilterAction filterAction = (SetFilterAction) action;

return state.withFilter(filterAction.getFilter());

}

...

return state;}

state

newTodo filter

todos

todo1 todo2 ...

State calcState(State state, Action action) { return State.build()

.withNewTodo( calcNewToDoState(state.newTodo, action) )

.withTodos( calcTodosState(state.todos, action) )

.withFilter( calcFilterState(state.filter, action) )

.create();}

Virtual SceneGraph

Action

StateVirtualSceneGraph

Frame-work

View Creator

Reducer

class VNode {

private final Class<?> nodeClass;

private final Map<String, Array<VNode>> childrenMap; private final Map<String, VNode> singleChildMap;

private final Map<String, VProperty> properties;

private final Map<VEventType, VEventHandler> eventHandlers;

...}

myVBox : VNode

nodeClass = VBox.classproperties = { spacing = 20}

myVBox : VNode

nodeClass = Label.classproperties = { prefWidth = 210, text = “You ...”}

myButton : VNode

nodeClass = Button.classproperties = { text = “Click Me”}eventHandlers = { … }

public VNode view(State state) {

return VBox() .spacing(20) .children( Label() .text(String.format( "You clicked the button %d times", state.getCounter() )), Button() .text("Click Me!"), .onAction(e -> Actions.incCounter()); );}

Application

Action

StateVirtualSceneGraph

Frame-work

Action Creator

View Creator

Reducer

Event

Redux with FXML

Why FXML?Pros:

- well-known by JavaFX developers- SceneBuilder

Cons:

- Stateful → hard to integrate in a functional approach like redux

FXML<VBox fx:controller="HelloController">

<children><Label fx:id="helloLabel"/><Button

text="Greet" onAction="#greetings"

/></children>

</VBox>

public class HelloController {

@FXMLprivate Label helloLabel;

public void greetings() {helloLabel.setText(

"Hello Sir or Madame");}

}

Let's see how others are doing it!→ Angular + Redux.JS

Idea- don't rerender whole UI on every update- keep UI stateful- connect to redux store and just update UI on changes

Angular

@Component({selector: 'app-hello',template: '

<div><p>{{ helloLabel }}</p>

</div>'

})class HelloComponent {

public helloLabel: String

}

Angular Component (Controller)

@Component({selector: 'app-hello',template: '

<div><p>{{ helloLabel }}</p>

</div>'

})class HelloComponent {

public helloLabel: Observable<String>

}

Change Type to "Observable" (Rx)

@Component({selector: 'app-hello',template: '

<div><p>{{ helloLabel }}</p>

</div>'

})class HelloComponent {

@select(state => state.getGreeting())public helloLabel: Observable<String>

}

Framework magic:Connect observable to redux store

@Component({selector: 'app-hello',template: '

<div><p>{{ helloLabel }}</p>

</div>'

})class HelloComponent {

@select(state => state.getGreeting())public helloLabel: Observable<String>

}

selector function:which value from the store should be used?

@Component({selector: 'app-hello',template: '

<div><p>{{ helloLabel | async}}</p>

</div>'

})class HelloComponent {

@select(state => state.getGreeting())public helloLabel: Observable<String>

}

bind field to template

Adopt this for JavaFX

public class HelloController {

@FXML private Label helloLabel;

}

public class HelloController {@FXML private Label helloLabel;

@Injectprivate Selector<AppState> selector;

}

Selector is a framework utility

public class HelloController {@FXML private Label helloLabel;

@Injectprivate Selector<AppState> selector;

public void initialize() {ObservableValue<String> value = selector.select(

state -> state.getGreeting());

}}

JavaFX ObservableValue

public class HelloController {@FXML private Label helloLabel;

@Injectprivate Selector<AppState> selector;

public void initialize() {ObservableValue<String> value = selector.select(

state -> state.getGreeting());

helloLabel.textProperty().bind(value);}

}

Debugging / DevTool

Debugging with Redux- whole application state at one specific location → easy to find out "what's the

current state of the app"- each interaction within the app is explicit by using actions → actions can be

recorded to see what happened

Redux-Javafx-DevtoolGithub: https://github.com/lestard/redux-javafx-devtool

- current state of the application- all actions- time-travel through action history

User interaction → Are the correct actions created?

ActionCreator / UI component

Is the state correct?

Does the selector yields correct values?

Bug

no

yes

reducerno

yes

selectorno

rendering of the UI component

yes

error source

ReduxFX:

Redux JavaFX DevTool:

https://github.com/netopyr/reduxfx

https://github.com/lestard/redux-javafx-devtool