They are not canon.
Spike is not complete.
Links to resources at end.
Using MVVM scaling complex components becomes hard.
Open sourced for 1 year.
Annoyed some Ember/Backbone core devs.
Angular/Ember looking to copy ideas.
Easy to integrate into existing projects.
An extension to JS exists whichs allows declarative code within a React component.
/** @jsx React.DOM */
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
React.renderComponent(<HelloMessage name="John" />, mountNode);
Don't be fooled this is still JS.
Optional.
No more templates.
"Separation of technology not concerns."
render: function() {
var commentNodes = this.props.data.map(function (comment) {
return (
<Comment author={comment.author}>
{comment.text}
</Comment>
);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
Any JS code will work.
So it's a lot more powerful then just templates.
Only one element can be returned from render.
propTypes: {
initialAmount: React.PropTypes.any.isRequired,
dealtAsset: React.PropTypes.string.isRequired,
amountIsValid: React.PropTypes.bool.isRequired,
amountErrorMessage: React.PropTypes.any
},
Sometimes components need internal state
Internal state is accessed via this.state
Internal state is modified via this.setState
/** @jsx React.DOM */
var Timer = React.createClass({
getInitialState: function() {
return {secondsElapsed: 0};
},
tick: function() {
this.setState({secondsElapsed: this.state.secondsElapsed + 1});
},
componentDidMount: function() {
this.interval = setInterval(this.tick, 1000);
},
render: function() {
return (
<div>Seconds Elapsed: {this.state.secondsElapsed}</div>
);
}
});
React.renderComponent(<Timer />, mountNode);
A component and all it's children will be re-rendered
When the data changes, React conceptually hits the "refresh" button.
How can this be done with complex components and not significantly impact performance?
Current user state such as focus or scrolling?
React compares two virtual DOM trees to find the minimum number of operations to move between the two.
Providing keys helps React understand when it's dealing with a different node...
render: function() {
var TenorLadderRowDate = caplinx.fxexecution.reacttile.components.tenorladder.TenorLadderRowDate;
var TenorLadderRowButton = caplinx.fxexecution.reacttile.components.tenorladder.TenorLadderRowButton;
return (
<div className="tenor-ladder-row">
<TenorLadderRowButton
ladderRowIndex={this.props.ladderRowIndex} side={'Bid'} />
<TenorLadderRowDate
ladderRowIndex={this.props.ladderRowIndex} />
<TenorLadderRowButton
ladderRowIndex={this.props.ladderRowIndex} side={'Ask'} />
</div>
);
}
Inside the TenorLadderRow React component.
You can return a tree of components. This is what makes React composable: a key tenet of maintainable frontends.
Data can't just flow downwards.
Users act upon child components.
Sometimes parent components (View Controllers) need to be notified.
ExecuteButton component needs to notify its parent that it's been clicked.
render: function() {
return (
<ExecuteButton
onExecute={this._executeTrade}
/>
);
},
_executeTrade: function() {
console.log('Executed');
}
render: function() {
return (
<button onClick={this._onClick}>
{buttonValue}
</button>
);
},
_onClick: function() {
var rateData = {
...
};
this.props.onExecute(rateData);
}
ExecuteButton
TenorLadderRowButton
If you are building small to medium applications React on it's own might be enough.
In larger applications where you need to share data between many React components though...
Data has to be migrated upwards until a shared ancestor is found that can pass data back down.
Yuck.
Application Architecture for Building User Interfaces
...is more of a pattern than a framework
Flux applications have three major parts: the dispatcher, the stores, and the views
Flux only allows unidirectional data flow.
It's a half-duplex communication system.
Data flow cannot loop back around.
They are the data model layer.
They create, maintain and dispose of subscriptions to server side data.
TenorLadderRowUtility.prototype._onSettlementDateUpdate = function(assetPair, settlementDate) {
this._tenorLadderRowActionCreator.settlementDateReceived(settlementDate);
};
To know how they should control subscriptions they need to listen to the dispatcher.
ActionCreators provide a semantic dispatcher API.
Facilitate passing data to the dispatcher.
They pass data to the dispatcher in the form of an action.
Actions are the only concept lacking their own class/module. They are simple object literals containing the action data and type.
TenorLadderRowActionCreator.prototype.settlementDateReceived = function(settlementDate) {
this._dispatcher.dispatch({
type: ActionTypes.TENOR_ROW_SETTLEMENT_DATE_RECEIVED,
settlementDate: settlementDate
});
};
Is essentially a global event bus that
You dispatch data via action creators and all listeners registered with the dispatcher are notified.
The domain model of the application. They contain the application's data and business logic.
They register with the Dispatcher.
Accept updates and reconcile them as appropriate.
They have no mutator methods.
You can only get state from them.
They are Event Emitters.
When their state changes they emit an event.
Listeners who are interested in the Store state can then retrieve it.
React components.
Expect with less downward flowing data.
The Stores help us out of our scaling problem!
React components can now register with the Stores to be notified of domain model changes.
Well it's not too different.
The only difference is it's not a utility calling the action creator.
The action creator is called by the view.
There is one issue with "classical" flux.
Once all the flux actors in an application are wired up data flow is simple.
But how do you wire them all up?
var ChatAppDispatcher = require('../dispatcher/ChatAppDispatcher');
var ChatWebAPIUtils = require('../utils/ChatWebAPIUtils');
var MessageStore = require('../stores/MessageStore');
module.exports = {
createMessage: function(text) {
ChatAppDispatcher.handleViewAction({
type: ActionTypes.CREATE_MESSAGE,
text: text
});
var message = MessageStore.getCreatedMessageData(text);
ChatWebAPIUtils.createMessage(message);
}
};
ChatMessageActionCreators
So we need some pattern for dealing with creating objects.
class TileDependenciesFactory {
constructor(dispatcher) {}
registerActionCreator(actionCreatorName, actionCreatorClass) {}
registerUtility(utilityName, utilityClass) {}
registerStore(storeName, storeClass) {}
getActionCreator(actionCreatorName) {}
getUtility(utilityName) {}
getStore(storeName) {}
getDispatcher {}
}
The react components are passed this in as a prop.
Factory passed into react components.
TileComponent.prototype.onOpen = function() {
var tileDispatcher = new Flux.Dispatcher();
this._tileDependenciesFactory = new TileDependenciesFactory(tileDispatcher);
this._populateFactory();
React.renderComponent(
<Tile factory={this._tileDependenciesFactory} />,
this._mountNode
);
};
TileComponent.prototype._populateFactory = function() {
this._tileDependenciesFactory.registerActionCreator('TenorLadderRow', TenorLadderRowActionCreator);
this._tileDependenciesFactory.registerUtility('TenorLadderRow', TenorLadderRowUtility);
this._tileDependenciesFactory.registerStore('TenorLadderRowDate', TenorLadderRowDateStore);
this._tileDependenciesFactory.registerStore('TenorLadderRowButton', TenorLadderRowButtonStore);
};
componentWillMount: function() {
this.props.factory.getStore('TenorLadderRowButton').addChangeListener(this._onChange, this);
},
_onChange: function() {
this.setState(this._getStore().getState());
}
Factory allows wiring up without static references to classes.
Could allow clients to customise components easily.
Our components can be persisted.
This means our stores will have to return serialized data on initial construction.
How do we get that data into every store being used in a component?
So when a view is first rendered it gets the serialized state from the store and not some default initial state.
We represented component state as an object literal
tile = {
amount: 500,
dealtAsset: 'GBP',
assetPair: 'GBPUSD',
instrument: '/FX/GBPUSD',
tenorladder: [
{
date: {
tenor: "SPOT"
}
}
]
}
"way to manage a component's focus on just the state data that it needs to operate."
So when a store is created it's passed in a cursor that has a reference to the state that the store should be able to mutate.
"Purpose of this spike is the find out whether React provides sufficient productivity and performance gains to replace the existing Presenter/Knockout MVVM solution."
I would use flux/react over current competitors*
No real Ember or Angular experience.
http://www.code-experience.com/avoiding-event-chains-in-single-page-applications/
https://docs.google.com/presentation/d/1afMLTCpRxhJpurQ97VBHCZkLbR1TEsRnd3yyxuSQ5YY/edit#slide=id.p
https://github.com/facebook/flux
https://github.com/swannodette/om/wiki/Cursors
https://stash.caplin.com/projects/MOTIF/repos/fxtrader/branches
react-fxtile branch