Earlier this month I presented at dddbrisbane about simplifying UI programming and I included the simplest React + Redux application I could come up with.
The simplest React + Redux application I could come up with
It displays a random number. Clicking the text generates a new random number. Toggle the Result
button to see the code more easily.
See the Pen FknRooowwwmmm by Liam McLennan (@liammclennan) on CodePen.
It uses the React function component syntax to define the component as a function from UI state (props
) to HTML (a H1 tag).
function HelloNumber(props) {
function handleClick(e) {
store.dispatch({type:'MAKE_A_NEW_NUMBER'});
}
return <h1 onClick={handleClick}>A random number {props.number}</h1>;
}
The onClick
handler dispatches a MAKE_A_NEW_NUMBER
action to the redux store.
The UI state for the application is an object with a property number
:
{ number: Math.random() }
The redux reducer (function responsibile for applying actions to the state to make a new state) handles the MAKE_A_NEW_NUMBER
number by setting it to a new random number.
let store = Redux.createStore(function (state = { number: Math.random() }, action) {
switch (action.type) {
case 'MAKE_A_NEW_NUMBER':
return Object.assign({},state, {number:Math.random()});
default: return state;
}
});
Now dispatching an application has updated the state, but we still need to have state changes trigger the UI to update, which is done via the subscribe
method:
store.subscribe(()=>{
ReactDOM.render(
<HelloNumber number={store.getState().number} />,
document.getElementById('container')
);
});
and finally prime the whole thing by an initial dispatch
store.dispatch({type:'MAKE_A_NEW_NUMBER'});
This is not strictly necessary but it is an easy way to trigger the subscribe
callback and cause the initial render.
The simplest Angular 2 + Redux application I could come up with
What would the equivalent Angular 2 application look like?
I couldn’t find a simple way to develop an Angular 2 application in Codepen or similar so instead I worked locally, starting with the Angular 2 Quickstart.
To reproduce the random number display functionality in Angular 2 I found that I needed a component hierarchy (parent + child).
The child component contains the header and dispatches a NEW_RANDOM_NUMBER
action when clicked, just like the React component. Two values are passed into the child component, from the parent, via @Input()
decorators: the application state (appState
) and the redux store dispatch function (dispatch
).
import { Component,Input } from '@angular/core';
@Component({
selector: 'my-child',
template: `<h1 (click)="onClickMe()">A random number {{appState.number}}</h1>`,
})
export class ChildComponent {
@Input() appState:any;
@Input() dispatch: (action:any) => void;
onClickMe() {
this.dispatch({type:'NEW_RANDOM_NUMBER'});
}
}
The parent component (AppComponent
) creates the redux store, using the same reducer as the React version. The UI state is copied to a local field (appState
) and updated whenever the store’s state changes. The appState
and redux dispatch
function are bound to the child component (<my-child/>
).
import { Component,Input } from '@angular/core';
import { ChildComponent } from './child.component';
import { createStore } from 'redux'
interface HasANumber {
number: number;
}
interface Action {
type: string;
}
@Component({
selector: 'my-app',
template: `<my-child [appState]="appState" [dispatch]="store.dispatch"></my-child>`
})
export class AppComponent {
store = createStore<HasANumber>(function (state = {number:Math.random()}, action:Action) {
switch (action.type) {
case "NEW_RANDOM_NUMBER":
return {number:Math.random()};
default: return state;
}
});
appState = this.store.getState();
constructor() {
this.store.subscribe(()=> {
this.appState = this.store.getState();
});
}
}
Final Thoughts
Having Typescript easily available in Angular 2 was nice. Further, I found that Angular 2 is at its best when imitating my very strict React style. Both frameworks work well with Redux and the uni-directional data flow idea.