Reputation: 7047
Learning React, I'm building a simple Calculator. Components model is:
- Calculator
- Sliders (input[ref=cars])
- Dashboard (p {this.state.cars})
Sliders
is component with set of inputs, that should be transfered to Dashboard
via parent Calculator
state.
My goal is to make state value in Dashboard
change on Sliders
s input component change.
var Calculator = React.createClass({
getInitialState: function() {
return {
cars: 0
};
},
render: function () {
return (
<div className="calculator">
<Sliders
cars={this.state.cars}
/>
<Dashboard
cars={this.state.cars}
/>
</div>
)
}
});
var Dashboard = React.createClass({
getInitialState:function () {
return {
cars: this.props.cars
}
},
render: function () {
return (
<div>
<h1>Dashboard</h1>
<p>Cars: {this.state.cars}</p>
</div>
)
}
})
var Sliders = React.createClass({
getInitialState: function() {
return {
cars: this.props.cars
};
},
handleInput: function () {
var state = {
cars: this.refs.cars.value
}
this.setState(state);
},
render: function () {
return (
<div className="sliders">
<input type="text" ref="cars" onChange={this.handleInput} value={this.state.cars}/>
</div>
)
}
})
As I've got, handleInput()
is trigged, so state for Sliders
is set. However, it's NOT "transfered" to parent Calculator
's state as 'cars'. So, Calculator
's state is not updated => Dashboard
state is not udpated too.
How to share Calculator's parent state between Sliders and Dashboard?
I know, there are plenty of similar questions on SO, however it's hard to find the specific helpful case, especially when you're complete noob in React. So, sorry for duplicating.
Upvotes: 3
Views: 577
Reputation: 816262
Components don't share state. The state of one component can become the props of another component though.
In your case, cars
should be the state of Calculator,
and Slide
and Dashboard
should only retrieve cars
as props. If the input changes, Sliders
should notify Calculator
about the change so that it can update its state and rerender (which causes Sliders
and Calculator
to update as well):
var Calculator = React.createClass({
getInitialState: function() {
return {
cars: 0
};
},
onUpdate: function (cars) {
this.setState({cars});
},
render: function () {
return (
<div className="calculator">
<Sliders
cars={this.state.cars}
onUpdate={this.onUpdate}
/>
<Dashboard
cars={this.state.cars}
/>
</div>
)
}
});
var Dashboard = React.createClass({
render: function () {
return (
<div>
<h1>Dashboard</h1>
<p>Cars: {this.props.cars}</p>
</div>
)
}
})
var Sliders = React.createClass({
handleInput: function (event) {
this.props.onUpdate(event.target.value);
},
render: function () {
return (
<div className="sliders">
<input type="text" ref="cars" onChange={this.handleInput} value={this.props.cars}/>
</div>
)
}
})
As few components as possible should have state and state should be as high up in the tree as possible. See also Props in getInitialState Is an Anti-Pattern
Upvotes: 4
Reputation: 9412
Pass parent as a prop to slider, and you wont need cars as a state in sliders.
This is one way of doing it, but FelixKing's answer is better.
var Calculator = React.createClass({
getInitialState: function() {
return {
cars: 0
};
},
handleInput: function () {
var state = {
cars: this.refs.cars.value
}
this.setState(state);
},
render: function () {
return (
<div className="calculator">
<Sliders
cars={this.state.cars} parent={this}
/>
<Dashboard
cars={this.state.cars}
/>
</div>
)
}
});
var Dashboard = React.createClass({
getInitialState:function () {
return {
cars: this.props.cars
}
},
render: function () {
return (
<div>
<h1>Dashboard</h1>
<p>Cars: {this.state.cars}</p>
</div>
)
}
})
var Sliders = React.createClass({
getInitialState: function() {
return {};
},
handleInput: function (value) {
this.props.parent.handleInput(this.refs.cars.value);
},
render: function () {
return (
<div className="sliders">
<input type="text" ref="cars" onChange={this.handleInput} value={this.props.cars}/>
</div>
)
}
})
Upvotes: 1
Reputation: 7666
In React you want to have one or several "smart" components, that handle state and "dumb" components. The "smart" components transfer the state down to the "dumb" component. The also inject event handlers, like in your case a handler for changing the amount of cars. Your calculator is your smart component. It should also have a function to handle the state change.
handleCarsChange: function( newAmount ) {
this.setState({ cars: newAmount });
},
Also don't make your initial state from props
(more Information). Props are made to change, use them directly in your render function like so:
var Dashboard = React.createClass({
render: function () {
return (
<div>
<h1>Dashboard</h1>
<p>Cars: {this.props.cars}</p>
</div>
)
}
});
Or with the React 14 component function syntax:
var Dashboard = function(props) {
return (
<div>
<h1>Dashboard</h1>
<p>Cars: {props.cars}</p>
</div>
);
};
Last but not least give the Sliders
component a handler function to deal with the state change:
<Sliders
cars={this.state.cars}
handleCarsChange={this.handleCarsChange}
/>
And change the handleInput
function in the Slider
component:
handleInput: function(value) {
this.props.handleCarsChange(this.refs.cars.value);
},
Upvotes: 1