Skip to content

Instantly share code, notes, and snippets.

@andyjessop
Last active September 30, 2016 15:12
Show Gist options
  • Save andyjessop/a92dccddbe3a7c0f1ca68140bd9a9af0 to your computer and use it in GitHub Desktop.
Save andyjessop/a92dccddbe3a7c0f1ca68140bd9a9af0 to your computer and use it in GitHub Desktop.

React/RxJS state management

// Lib
import React, {Component} from 'react';
import Bootstrap from 'bootstrap/dist/css/bootstrap.css';
// Services
import TextBoxService from './text-box/text-box.service';
// Containers
import BaseContainer from './base/base.container';
// Components
import TextBoxContainer from './text-box/text-box.container';
export default class App extends BaseContainer {
constructor(props) {
super(props);
// The state is defined here because it's the highest node where the textBox
// data is required
this.state = {
textBoxes: []
};
this.services = {
textBox: new TextBoxService()
};
}
componentWillMount() {
// Subscribe to the service - this automatically updates the state
// when anything changes
this.services.textBox.emitter.subscribe(textBoxes => {
this.setState(prevState => ({
textBoxes: textBoxes
}));
});
}
render() {
return (
<div className="container">
<h3>A React example with <code>{this.state.textBoxes.length} TextBox</code> components</h3><br/>
<div className="row">
<div className="col-sm-6">
<TextBoxContainer textBoxes={this.state.textBoxes} services={this.services}/>
<button className="btn btn-primary" onClick={this.services.textBox.addTextBox}>Add Component</button>
</div>
</div>
</div>
);
}
}
// Lib
import React, {Component} from 'react';
import lodash from 'lodash';
export default class TextBox extends Component {
constructor() {
super();
this.remove = this.remove.bind(this);
this.update = this.update.bind(this);
}
update(event) {
this.props.services.textBox.updateText(event);
}
remove() {
this.props.services.textBox.removeTextBox(this.props.id);
}
render() {
return (
<div id={this.props.id} className="row form-group">
<div className="col-sm-9">
<input
className="form-control"
type="text"
id={this.props.id}
value={this.props.value}
placeholder={this.props.name}
onChange={this.update}
/>
<span className="help-block">{this.props.value}</span>
</div>
<div className="col-sm-1">
<button className="close" onClick={this.remove}><span>&times; </span></button>
</div>
</div>
);
}
}
// Lib
import React, {Component, PropTypes} from 'react';
import {each} from 'lodash';
// Containers
import BaseContainer from '../base/base.container';
// Components
import TextBox from './text-box.component.jsx';
export default class TextBoxContainer extends BaseContainer {
render() {
if (this.props.textBoxes.length) {
const boxes = [];
each(this.props.textBoxes, (box) =>
boxes.push(
<TextBox
key={box.id}
name={box.name}
id={box.id}
value={box.value}
services={this.props.services}
/>
));
return (<div>{boxes}</div>);
} else {
return null;
}
}
}
// Lib
import {Subject} from 'rxjs/Subject';
import {findIndex} from 'lodash';
export default class TextBoxService {
/**
* The service is the distinct source of truth for the state
* of all text boxes defined in the container where it is instantiated.
*
* It ensures uni-directional flow by using an emitter to emit
* the entire state of the text boxes on each change.
*/
constructor() {
this.emitter = new Subject();
this.textBoxes = [];
this.nextId = 1;
// Bindings
this.addTextBox = this.addTextBox.bind(this);
}
addTextBox(value) {
this.textBoxes = this.textBoxes.concat({
id: this.nextId,
name: `TextBox ${this.nextId}`,
value: value ? value : ''
});
// Emit to listeners
this.emitter.next(this.textBoxes);
this.nextId++;
};
removeTextBox(id) {
// Get textBox index
const index = findIndex(this.textBoxes, function(textBox) { return textBox.id === id });
// Remove
this.textBoxes.splice(index, 1);
// Emit to listeners
this.emitter.next(this.textBoxes);
};
updateText(event) {
// Get textBox index
const index = findIndex(this.textBoxes, function(textBox) { return textBox.id === id });
this.textBoxes(index).value = event.target.value;
// Emit to listeners
this.emitter.next(this.textBoxes);
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment