I have a Rails app with react-rails
gem.
This is my controller:
class WebsitesController < ApplicationController
def index
@websites = Website.all
end
end
This is my view:
<%= react_component('WebsiteList', websites: @websites) %>
This is my React component wit:
class WebsiteList extends React.Component {
render () {
return (
<div className="container">
<AddWebsite/>
<WebsiteItem websites={this.props.websites}/>
</div>
);
}
}
WebsiteList.propTypes = {
websites: React.PropTypes.node
};
In WebsiteItem it's just mapping the array and showing each object.
AddWebsite Component with ajax to get data on the server:
class AddWebsite extends React.Component {
constructor() {
super();
this.state = {
formValue: "http://www."
}
}
handleChange(e) {
this.setState({formValue: e.target.value});
}
submitForm() {
$.ajax({
method: "POST",
url: "/websites",
data: { website: {name: "New Web", url: this.state.formValue} }
})
.done(function() {
console.log("done")
});
}
render() {
return (
<form>
<input value={this.state.formValue} onChange={this.handleChange.bind(this)} name="url"/>
<button type="button" onClick={this.submitForm.bind(this)} className="btn">Add Web</button>
</form>
);
}
}
When someone adds (or delete) a link and ajax is success, I'd like to update the React component as well. Instead of this.props.websites & propTypes - how I could do it for state? Do I need to get the it from ajax or can I use the <%=react_component ... %>
in erb?
What is the best way to solve it and create basically a single page app?
Ok, so what you really want is to have a callback in the AddWebsite
component passed from the Dashboard
where you hold the app state. Please note that the this.state
is component bound which means you can not access state of another component. You can pass the state of one component as a property to another though and that's what we're going to use.
// dashboard.es6.jsx
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
websites: this.props.websites
}
// Don't forget to bind to self
this.handleWebsiteAdd = this.handleWebsiteAdd.bind(this)
}
handleWebsiteAdd(newWebsite) {
const websites = this.state.websites.concat([newWebsite]);
this.setState({websites});
}
render() {
return (
<div className="container">
<AddWebsite onAdd={this.handleWebsiteAdd}/>
<WebsiteList websites={this.state.websites}/>
</div>
);
}
}
// add_website.es6.jsx
class AddWebsite extends React.Component {
constructor() {
super();
this.state = {
formValue: "http://www."
}
}
handleChange(e) {
this.setState({formValue: e.target.value});
}
submitForm() {
$.ajax({
method: "POST",
url: "/websites",
data: { website: {name: "New Web", url: this.state.formValue} }
})
.done(function(data) {
// NOTE: Make sure that your API endpoint returns new website object
// Also if your response is structured differently you may need to extract it
// and instead of passing data directly, pass one of its properties.
this.props.onAdd(data)
Materialize.toast('We added your website', 4000)
}.bind(this))
.fail(function() {
Materialize.toast('Not a responsive URL', 4000)
});
}
render() {
return (
<div className="card">
<div className="input-field" id="url-form">
<h5>Add new website</h5>
<form>
<input id="url-input" value={this.state.formValue} onChange={this.handleChange.bind(this)} type="text" name="url"/>
<button type="button" onClick={this.submitForm.bind(this)} className="btn">Add Web</button>
</form>
</div>
</div>
);
}
}
AddWebsite.propTypes = {
onAdd: React.PropTypes.func.isRequired
};
Please note this is just one of the many possible solutions.