HOW,
WHY,
WHAT,
WHERE
Another React spike?
- How do you build your components?
- Why build them like that?
- What tools do you use?
- Where do the component parts go?
We know React works.
How
- View
- Domain model (business logic)
- Data model (server data)
Sensible guidelines to follow when creating new React components.
Components should be the composition of three layers.
Historically we have made the view or data model "fat" and neglected or ignored the domain layer.
How
Already part answered by the React team.
Thinking in React*
http://facebook.github.io/react/docs/thinking-in-react.html
Start with a mock
-
Break the UI into a component hierarchy
-
Build a static version in React
-
Identify the minimal representation of UI state
-
Identify where your state should live
-
Add inverse data flow
import React from "react";
import "./FXAmountInputBox.css";
export default ({disabled, enteredValue, handleChange}) => (
<input
className="FXAmountInputBox"
disabled={disabled}
value={enteredValue}
onChange={({target: {value}}) => handleChange(value)}
/>
);
import React from "react";
import FXAmountInputBox from "../FXAmountInputBox-react";
import ToggleButton from "../ToggleButton-react";
import "./FXAmountAndDealtCurrencyRow.css";
export default (
{amount, disabled, dealtCurrency, handleDealtCurrencyToggle, handleAmountChange}
) => (
<div className="FXAmountAndDealtCurrencyRow">
<FXAmountInputBox disabled={disabled}
enteredValue={amount} handleChange={handleAmountChange} />
<ToggleButton disabled={disabled}
handleToggle={handleDealtCurrencyToggle} toggleText={dealtCurrency} />
</div>
);
Why
Separation of Concerns
What
Flux is no longer the simplest domain model solution.
The state tree/atom model has replaced it as the current preferred solution.
The domain model state is contained in a stand alone tree data structure.
Stateless React components with props passed in.
What
The data is bound to the components.
The data tree and component tree can/usually do have different topologies
// The initial state of the application
const model = Model({
currencyPair: "EURUSD",
amount: 500,
dealtCurrency: "EUR",
settlementDate: "15/10/2015",
tenor: "(SPOT)",
subjectData: {
bidValue: "1.13785",
askValue: "1.13825"
}
});
What
https://github.com/Yomguithereal/baobab-react
https://github.com/christianalfoni/cerebral
https://github.com/rackt/redux
import {render} from "react-dom";
import Controller from "cerebral";
import Model from "cerebral-baobab";
import {Container} from "cerebral-react";
// Any utils you want each action to receive
const services = {
sljs: "sljs"
};
// Your cerebral instance
const controller = Controller(model, services);
function dealtCurrencyToggled(input, state) {
const currencyPair = state.get("currencyPair");
const dealtCurrency = state.get("dealtCurrency");
const newDealtCurrency = currencyPair.replace(dealtCurrency, "");
state.set("dealtCurrency", newDealtCurrency);
}
...
controller.signal("dealtCurrencyToggled", [
dealtCurrencyToggled, clearPrices, subscribeToNewSubject
]);
controller.signal("amountChanged", [amountChanged, clearPrices, subscribeToNewSubject]);
...
render(
<Container controller={controller}>
<FXTile />
</Container>,
document.getElementById("cerebral-container")
);
import React from "react";
import {Component} from "cerebral-react";
import FXTileHeaderBar from "../components/fxtile/FXTileHeaderBar-react";
import TwoWayFXTile from "./twowaytile/TwoWayFXTileCerebralWrapper-react";
const statePaths = {
currencyPair: ["currencyPair"]
};
const FXTile = ({currencyPair}) => (
<div>
<FXTileHeaderBar currencyPair={currencyPair}/>
<TwoWayFXTile />
</div>
);
export default Component(statePaths, FXTile);
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './containers/App';
import todoApp from './reducers';
let store = createStore(todoApp);
let rootElement = document.getElementById('root');
render(
<Provider store={store}>
<App />
</Provider>,
rootElement
);
Redux
import { connect } from 'react-redux';
// Which props do we want to inject, given the global state?
// Note: use https://github.com/faassen/reselect for better performance.
function select(state) {
return {
visibleTodos: selectTodos(state.todos, state.visibilityFilter),
visibilityFilter: state.visibilityFilter
};
}
// Wrap the component to inject dispatch and state into it
export default connect(select)(App);
import React, {Component} from 'react';
import {render} from 'react-dom';
import {root} from 'baobab-react/higher-order';
import tree from './state';
// We will write this component later
import List from './list.jsx';
// Creating our top-level component
class App extends Component {
render() {
return <List />;
}
}
// Let's bind the component to the tree through the `root` higher-order component
const RootedApp = root(App, tree);
// Rendering the app
render(<RootedApp />, document.querySelector('#mount'));
Baobab-react
import React, {Component} from 'react';
import {branch} from 'baobab-react/higher-order';
class List extends Component {
render() {
return <div>{this.props.colors.length}</div>;
}
}
// Branching the component by mapping the desired data to cursors
export default branch(List, {
cursors: {
colors: ['colors']
}
});
Given a component and a tree data structure bind certain paths in the tree to the component.
But that's not the end!
There is another type of state binding library.
These can also fetch and cache remote data.
Sort of a StreamLinkJS replacement.
const Title = React.createClass({
displayName: "Title",
statics: {
queries: {
movie() {
return ["title"];
}
}
},
render() {
return <h1>{this.props.title}</h1>
}
});
Falcor
Falcor automatically traverses references in your graph and makes requests as needed. Falcor transparently handles all network communications, opportunistically batching and de-duping requests.
import model from "./falcor-model";
const MovieList = React.createClass({
displayName: "MovieList",
getInitialState() {
return {
movies: []
}
},
componentDidMount() {
model
.get(["movies", {from:0, to: 10}, Movie.queries.movie()])
.subscribe( (data) => {
this.setState({movies: _.values(data.json.movies);
});
});
},
render() {
return (
<div>
{this.state.movies.map( el => <Movie movie={el} /> )}
</div>
);
}
});
Relay + GraphQL
class Tea extends React.Component {
render() {
var {name, steepingTime} = this.props.tea;
return (
<li key={name}>
{name} (<em>{steepingTime} min</em>)
</li>
);
}
}
Tea = Relay.createContainer(Tea, {
fragments: {
tea: () => Relay.QL`
fragment on Tea {
name,
steepingTime,
}
`,
},
});
ReactDOM.render(
<Relay.RootContainer
Component={TeaStore}
route={new TeaHomeRoute()}
/>,
mountNode
);
Which one should we use?
Are the data fetching libraries the future?
HOW,
By briandipalma
HOW,
- 766