redux with javafx - rainfocus...debugging with redux - whole application state at one specific...
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