When the click event takes place, the state is not set to the value given inside the CompOne. It still show the initial state and console logs the old state which is "hello".
var CompOne = React.createClass({
getInitialState: function() {
return {
edit: "hello"
}
},
editme: function () {
this.setState({
edit: "there"
})
console.log(this.state.edit)
},
render: function(){
return (
<div>
{this.props.name}
<button onClick={this.editme}>Edit</button>
</div>
)
}
})
var Pri = React.createClass({
render: function () {
return (
<div>
< CompOne name = "Anne"/>
< CompOne name = "Bob"/>
</div>
);
}
})
ReactDOM.render( <Pri /> , document.getElementById("root"));
Function setState is not synchronous. Here is a note about this from React documentation;
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls
may be batched for performance gains.
In human terms this means that if you call setState and try to read the state immediately, the state could be changed or it could be the same.
The solution that you can use is to pass a callback to setState method as a second parameter:
editme: function () {
this.setState({
edit: "there"
}, function(){
// this function would be invoked only when the state is changed
console.log(this.state.edit);
});
}
The purpose of the second parameter is described in the same documentation article:
The second parameter is an optional callback function that will be
executed once setState is completed and the component is
re-rendered.
You need to use the callback function in setState because setState takes time to mutate and you console.log gets executed before the state is mutated as statements are executed asynchronously.
editme: function () {
this.setState({
edit: "there"
}, function(){
console.log(this.state.edit)
})
},
var CompOne = React.createClass({
getInitialState: function() {
return {
edit: "hello"
}
},
editme: function () {
this.setState({
edit: "there"
}, function(){
console.log(this.state.edit)
})
},
render: function(){
return (
<div>
{this.props.name}
<button onClick={this.editme}>Edit</button>
</div>
)
}
})
var Pri = React.createClass({
render: function () {
return (
<div>
< CompOne name = "Anne"/>
< CompOne name = "Bob"/>
</div>
);
}
})
ReactDOM.render( <Pri /> , document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="root"></div>
Related
I'm trying to create a small react component, however, I am unable to set the state. Below is my code. In the _onChange function, I am trying to set an array of length 10 to State and console.log the same. I am getting an empty array in the console.
var Home = React.createClass({
getInitialState: function () {
return ({
reviewData: []
});
},
componentWillMount: function() {
ReviewStore.addChangeListener(this._onChange);
ReviewAction.getRatings();
console.log(this.state.reviewData);
},
_onChange: function() {
var res = ReviewStore.getRating();
console.log(res); //Here I am getting array of length 10
this.setState({reviewData: ReviewStore.getRating()});
console.log(this.state.reviewData); //Here I am getting array of length 0
},
componentWillUnmount: function () {
ReviewStore.removeChangeListener(this._onChange);
},
ratingChanged : function(newRating) {
console.log(newRating)
},
render: function() {
return(
<div>
<h2>Rating of Arlo Smart Home 1 HD Camera</h2>
<hr/>
<h4>Average Rating: </h4><ReactStars half={false} onChange={this.ratingChanged} size={24}/>
</div>
)
}
});
setState is asynchronous. The value will not be set immediately. You can pass a callback to setState which will be called when new state is set.
From react documentation https://facebook.github.io/react/docs/component-api.html
setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.
You can change your code
this.setState({reviewData: ReviewStore.getRating()}, function () {
console.log(this.state.reviewData)
});
I would like to use setTimeout with React.js before changing state in a function. I would like to use it in the onHeaderTyped function. I've tried including the TimerMixin but I received an error (it was undefined). Any other routes?
Typing = React.createClass({
getInitialState: function() {
return { showFirst: true, showSecond: false, showThird: false };
},
onHeaderTyped: function() {
this.setState({showFirst: false});
this.setState({showSecond: true});
},
onSecondTyped: function() {
this.setState({showSecond: false});
this.setState({showThird: true});
},
onThirdTyped: function() {
this.setState({showThird: false});
this.setState({showFirst: true});
},
render: function() {
const docs = '#one';
return (
<div>
<div className="TypistExample">
{this.state.showFirst ? (
<Typist className="TypistExample-header" avgTypingSpeed={100} startDelay={1000}
onTypingDone={this.onHeaderTyped} cursor={{hideWhenDone: true}}>
<h1><a href={docs}>First Stuff</a></h1>
</Typist>
) : null }
{this.state.showSecond ? (
<Typist className="TypistExample-header" avgTypingSpeed={100} startDelay={1000}
onTypingDone={this.onSecondTyped}>
<h1> Some Stuff </h1>
</Typist>
) : null }
{this.state.showThird ? (
<Typist className="TypistExample-header" avgTypingSpeed={100} startDelay={1000}
onTypingDone={this.onThirdTyped}>
<h1> More Stuff </h1>
</Typist>
) : null }
</div>
</div>
);
}
});
As an aside, while running this, I am getting this error:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the undefined component.
The setState call in question is working despite the warning. The warning only occurs after the component on which I am calling setState has been removed, and then rerendered. Why am I getting this warning when the component is already mounted?
<div id="contentArea">
{this.state.activePlugin?this.state.activePlugin.render():null}
</div>
the render method returns a react element:
render(){
return <DatasetsList />;
}
state.activePlugin is set to null and the component containing the jsx code above is rerendered, this is how removing the 'activePlugin' happens. When render is called again and returns the <DatasetsList /> for the second time, subsequent clicks inside the component produce this warning, despite the fact that the setState calls they initiate in fact work as expected.
In other words, the warning appears to be misleading, as the code is working as expected.
componentDidMount() {
this.unsubscribe = this.props.store.listen(s=>{
this.setState(s);
});
}
https://jsfiddle.net/taylorallred/69z2wepo/29874/
var Hello = React.createClass({
getInitialState: function() {
return {
activePlugin: Plugin
}
},
togglePlugin: function() {
this.setState({
activePlugin: this.state.activePlugin ? null : Plugin
})
},
render: function() {
return <div >
< div onClick = {
this.togglePlugin
} > Hello {
this.props.name
} < /div> < div > {
this.state.activePlugin ? < Plugin / > : null
} < /div> < /div>;
}
});
var Plugin = React.createClass({
render: function() {
return ( < MyComponent / > );
}
});
var MyComponent = React.createClass({
getInitialState:function(){
return {a: 1};
},
componentDidMount:function(){
window.setInterval(function(that){that.setState({a: that.state.a++})}, 2000, this);
},
render: function() {
var a = this.state.a;
return <div > MyComponent { a }< /div>;
}
});
ReactDOM.render( < Hello name = "World" / > ,
document.getElementById('container')
);
It's probably because your component is receiving updates when it's not rendered and that component is trying to render because it's state changed. See the fiddle. Click to remove the component and you'll get that error in the console.
The warning was referring to the old unmounted instance of my component which was still listening to the store, as shown in the question. Simply unsubscribing from the store inside componentWillUnmount solved the issue.
componentWillUnmount() {
this.unsubscribe();
}
I want to pass a value to a component this way, but when I try to console log this.props.vouch it returns an undefined value.
I know it will work if I put:
<Something onClick={this.log} vouch=this.props.vouch />
and
ReactDOM.render(<List vouch="value 1"/>, document.getElementById('react-app'))
But I will want to use different vouch value later in the code and be able to reuse Something component.
var Something = React.createClass({
propTypes:{
vouch: React.PropTypes.string,
},
render: function() {
return (
<div>
<h1 onClick={this.props.onClick} vouch={this.props.vouch}>Click!</h1>
</div>
);
}
});
var List = React.createClass({
log: function() {
console.log(this.props.vouch);
},
render: function () {
return (
<Something onClick={this.log} vouch="value 1" />
<Something onClick={this.log} vouch="value 2" />
);
}
});
ReactDOM.render(<List />, document.getElementById('react-app'));
You can't set this.props from child component, but you can pass data using data attributes, like this
<h1 onClick={this.props.onClick} data-vouch={this.props.vouch}>Click!</h1>
...
log: function (e) {
console.log(e.target.dataset.vouch);
},
Example
or using .bind, like this
<h1 onClick={this.props.onClick.bind(null, this.props.vouch)}>Click!</h1>
...
log: function (vouch) {
console.log(vouch);
},
Example
or call callback in child component and pass props, like this
handleClick: function () {
this.props.onClick(this.props.vouch)
},
render: function() {
return (<div>
<h1 onClick={this.handleClick}>Click!</h1>
</div>)
}
...
log: function (vouch) {
console.log(vouch);
},
Example
You're not passing this.props.vouch to List, so your log will return undefined.
var Something = React.createClass({
propTypes:{
vouch: React.PropTypes.string,
},
onClick: function() {
this.props.onClick( this.props.vouch )
},
render: function() {
return (
<div>
<h1 onClick={this.onClick.bind( this )} vouch={this.props.vouch}>Click!</h1>
</div>
);
}
});
var List = React.createClass({
log: function( vouch ) {
console.log( vouch );
},
render: function () {
return this.props.vouch.map( vouch => <Something onClick={ log } vouch = { vouch } /> )
}
});
var vouch = [
{
value: 'foo'
},
{
value: 'bar'
}
]
ReactDOM.render(<List vouch={ vouch } />, document.getElementById('react-app'));
The actual problem of your log not working could also be solved by passing List.log to Something (which you do already) and then invoking it in the context of Something by using <h1 onClick={ this.props.onClick.call( this ) and having log console.log( this.props.vouch ) but this solution would be nasty from a maintainability standpoint.
It is important to understand the parent->child relationship between components that you are creating. At any point you can grab your vouch data and inject it but by injecting it at the List component you keep all children pure i.e. when you render you are passing the state of the system, you arent attempting to grab state or worse, mutate, state during the life-cycle of a render.
I have a component with a list of items. I want each time I add or remove an item from the list to have animation. Everything it's OK with that.
My only problem is that I want to disable the animation first time when the component initialize.
http://jsfiddle.net/kb3gN/6887/
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
var Hello = React.createClass({
getInitialState: function () {
return {items: []};
},
addColor:function(){
this.setState({items:this.state.items.concat('new color')});
},
componentDidMount: function () {
var items = ['blue', 'red', 'green'];
this.setState({items: items});
},
render: function () {
var contentList = this.state.items.map(function (i,k) {
return (<li>{i}</li>);
});
return (
<div>
Hello <button onClick={this.addColor}>add</button>
<ReactCSSTransitionGroup transitionName="example">
{contentList}
</ReactCSSTransitionGroup>
</div>
)
}
});
React.render(<Hello name="World" />, document.body);
So, how can I make the animation appear only when changing the list and not ont the very first setState from didMount method?
Using componentWillMount instead of componentDidMount it will work perfectly as shown in this fiddle : http://jsfiddle.net/ghislaindj/jpuyd3gb/1/
However, if your real code must fetch the data in DidMount, you should use ssorallen comment.