React/RxJS state management
Last active
September 30, 2016 15:12
-
-
Save andyjessop/a92dccddbe3a7c0f1ca68140bd9a9af0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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> | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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>× </span></button> | |
</div> | |
</div> | |
); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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