When this.state.selected changes, the select box changes its value correctly, but its onChange method is not called. This is probably because I need two-way-bindings for that, so I've tried the valueLink pattern from the docs, but it still doesn't get fired:
render: function() {
return (
React.createElement("select", { valueLink: { value: this.state.selected, requestChange: this.changeHandler } },
React.createElement("option", { value: 1 }, "1"),
React.createElement("option", { value: 2 }, "2")
)
)
}
See the whole fiddle: http://jsbin.com/tisahiliqi/1/edit?html,output
How can I get requestChange executed without firing an event manually (which is, in my opinion, what two-way-bindings should do for me)?
Did I misunderstand something?
Your code is a little mixed up with React's LinkedStateMixin. If you want to fire your own event handler you can use this:
http://jsbin.com/fuwowifoqe/1/
var MySelect = React.createClass({
getInitialState: function() {
return { selected: 1 }
},
componentDidMount: function() {
// Imagine some AJAX that takes 1s
window.setTimeout(function() {
this.setState({ selected: 2 });
}.bind(this), 1000);
},
changeHandler: function(e) {
// ... Doesn't fire...
alert("Something changed!");
this.setState({selected : e.target.value })
},
render: function() {
return (
React.createElement("select", { value: this.state.selected, onChange: this.changeHandler },
React.createElement("option", { value: 1 }, "1"),
React.createElement("option", { value: 2 }, "2")
)
)
}
});
React.render(React.createElement(MySelect, null, "Hi"), document.getElementById("app"));
Note that the handler is not fired when you change value with setState, but when value is changed via user interaction.
UPDATE
Then you can observe changes to this.state or this.state.selected using one of the life cycle methods such as componentWillUpdate or componentDidUpdate:
jsbin: http://jsbin.com/lipovucapi/1/
componentDidUpdate: function(prevProps, prevState){
if(this.state.selected != prevState.selected)
alert("selected changed!");
},
Related
I want to get the state inside of setTimeOut
change = () => {
setTimeout(function() {
this.setState({
categories: [...this.state.categories, { time: 20, msg: "msg1", visible:true }]
}) }, 3000);
};
in this this.state.categories i get the next error: "Cannot read property 'categories' of undefined"
That's because the callback you are providing to setTimeout is using the function keyword, which will have its own scope unless you bind it manually. You can fix that cleanly by making the callback an arrow function:
change = () => {
setTimeout(() => {
this.setState({
categories: [...this.state.categories, { time: 20, msg: "msg1", visible:true }]
})
}, 3000);
};
Based on your other comment, if you'd like to manually bind it instead, you can do this:
change = () => {
setTimeout(function () {
this.setState({
categories: [...this.state.categories, { time: 20, msg: "msg1", visible:true }]
})
}.bind(this), 3000);
};
Use arrow function, you missed the this binding. Arrow function will do this for you implicitly.
change = () => {
setTimeout(() => {
this.setState({
categories: [...this.state.categories, { time: 20, msg: "msg1", visible:true }]
}) }, 3000);
};
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 Clock component.
Why is this error keeping being displayed ?? When I log out from my app this warning starts to appear on the console. Could anybody explain why that happens ?
const Clock = React.createClass({
getInitialState() {
return {
currentTime: moment.utc().format(formatter)
};
},
componentDidMount: function() {
const tickId = setInterval(this.tick, 1000);
this.setState({tickId: tickId});
},
componentWillUnmount: function() {
clearInterval(this.state.tickId);
},
tick: function() {
this.setState({
currentTime: moment.utc().format(formatter)
});
},
render() {
return <span>{this.state.currentTime}</span>;
}
});
I've ran into a similar issue before. What was happening in my case, I was correctly canceling the interval, but there would be times where it would cancel after already calling the function again. What I needed was a way to know if the component was still mounted or not. Unfortunately, React doesn't expose a components mounted state (or at least not easily). So I ended up keeping track of the mounted state myself and then checking if the component was still mounted when calling my interval function.
const Clock = React.createClass({
getInitialState() {
return {
currentTime: moment.utc().format(formatter)
};
},
componentDidMount: function() {
this._isMounted = true;
const tickId = setInterval(this.tick, 1000);
this.setState({tickId: tickId});
},
componentWillUnmount: function() {
this._isMounted = false;
clearInterval(this.state.tickId);
},
tick: function() {
if(this._isMounted) {
this.setState({
currentTime: moment.utc().format(formatter)
});
}
},
render() {
return <span>{this.state.currentTime}</span>;
}
});
No need to store the tickId in state.
componentDidMount: function() {
this.tickId = setInterval(this.tick, 1000);
},
componentWillUnmount: function() {
clearInterval(this.tickId);
}
I am trying to create a Component whose content changes when the screen orientation(portrait/landscape) changes. This is what I am doing:
var Greeting = React.createClass({
getInitialState: function() {
return {orientation: true}
},
handleChange: function() {
if ('onorientationchange' in window) {
window.addEventListener("orientationchange", function() {
this.setState({
orientation: !this.state.orientation
})
console.log("onorientationchange");
}, false);
} else if ('onresize' in window) {
window.addEventListener("resize", function() {
this.setState({
orientation: !this.state.orientation
})
console.log("resize");
}, false);
}
},
render: function() {
var message = this.state.orientation ? "Hello" : "Good bye"
return <p>{message}</p>;
}
});
ReactDOM.render(
<Greeting/>, document.getElementById('container'));
How to make sure the state is mutated when the orientation change event is fired .
Your calling to this.setState is wrong. Need to change it to be like:
handleChange: function() {
var self = this; // Store `this` component outside the callback
if ('onorientationchange' in window) {
window.addEventListener("orientationchange", function() {
// `this` is now pointing to `window`, not the component. So use `self`.
self.setState({
orientation: !self.state.orientation
})
console.log("onorientationchange");
}, false);
}
I am a newbie in React.js. While trying to understand the lifecycles in React, i stumbled upon componentWillReceiveProps. Even though i got hold of other functions, i am still not able to figure out componentWillReceiveProps. I created a small snippet where on every button click, i am incrementing the variable 'val'. When val becomes a multiple of 5, i want to change the value of 'increasing', which i am not able to do.
My Code is:
var Increment = React.createClass({
getInitialState: function() {
return {val: 0, increasing: false};
},
componentWillMount: function() {
console.log("componentWillMount");
},
componentDidMount: function() {
console.log("componentDidMount");
},
handleClick: function() {
console.log("inHandleClick");
console.log(this.state.val);
this.setState({val: (this.state.val+1)});
},
componentWillReceiveProps : function(nextProps) {
this.setState({
increasing: (nextProps.val > this.props.val)
});
},
shouldComponentUpdate: function(nextProps, nextState) {
return (nextState.val % 5 ==0)
},
render: function() {
console.log(this.state.increasing);
return (
<div>
<button onClick={this.handleClick}>{this.state.val}</button>
</div>
);
}
});
ReactDOM.render(<Increment />, mountNode);
Any Leads? Thanks in advance
See this fiddle
var IncrementButton = React.createClass({
componentWillReceiveProps : function(nextProps) {
this.setState({
increasing: (nextProps.val > this.props.val)
});
},
render: function() {
return (<button onClick={this.props.handleClick}>{this.props.val}</button>);
}
});
var Increment = React.createClass({
getInitialState: function() {
return {val: 0, increasing: false};
},
handleClick: function() {
console.log("inHandleClick");
console.log(this.state.val);
this.setState({val: (this.state.val+1)});
},
shouldComponentUpdate: function(nextProps, nextState) {
return (nextState.val % 5 ==0)
},
render: function() {
console.log(this.state.increasing);
return (
<div>
<IncrementButton handleClick={this.handleClick} val={this.state.val}/>
</div>
);
}
});
React.render(<Increment />, mountNode);
(Thanks to #Aaron for a more accurate description below)
componentWillReceiveProps is called if your component's props get set; note it may be called even though the props haven't changed in value (You seem to take this into account with your greater than check). Since you want to compare a new value of props to an old value, you need the state to be managed outside your component. I have therefore, in this sample, split your component into two pieces by extracting the button.
Is there an established pattern used to manage user interactions with individual components, such as displaying loader spinners, disabling input fields while a form is saving/loading, etc.?
I'm finding myself doing the following in my stores in order to keep components somewhat decoupled from any implied state:
function CampaignStore() {
EventEmitter.call(this);
AppDispatcher.register(payload => {
switch (payload.type) {
// [#1] ---------------v (main action)
case CampaignContants.SAVE:
// [#2] ------------------------v (prepare for the main action)
this.emit(CampaignContants.WILL_SAVE);
const data = payload.data;
if (data.id) {
// [#3] ---v (perform main action in store)
updateCampaign(payload.data).then(_ => {
// [#4] ------------------------v (after main action)
this.emit(CampaignContants.DID_SAVE, 0)
});
} else {
insertCampaign(payload.data).then(campaignId => this.emit(CampaignContants.DID_SAVE, campaignId));
}
break;
// ...
}
}
}
Basically, I just fire an event saying that some action is about to take place, then I perform the action (make API call, etc.), then emit another event when the action completes.
Inside a component, I can just subscribe to a WILL_<action> event, render all the spinners and such, then clear up the screen when the DID_<action> is fired. While this seems to work, it does feel pretty boilerplattie and repetitive, as well as super messy (way too much state that only exists to tweak the UI based on where an action is (between WILL_<action> and *DID_<action>.
// some component
var MyComponent = React.createClass({
getInitialState: function () {
return {
items: [],
loading: false,
saving: false,
checkingPasswordStrength: fase,
// ...
};
},
render: function(){
return (
<div>
{this.state.loading && (
<p>Loading...</p>
)}
{!this.state.loading && (
// Display component in not-loading state
)}
</div>
);
}
});
I think you would be better off using the lifecycle methods such as componentWillMount, componentDidMount, componentWillUpdate, and componentWillUnmount. Using those methods you can inspect the previous/current/next props/state (depending on the method) and respond to that. That way your store only handles your state, and your components become more pure.
we have found a simple loading container component helps here.
so something like this:
const LoadingContainer = React.createClass({
getDefaultProps: function() {
return {isLoadedCheck:(res) => res.data!=null }
},
getInitialState: function() {
return {isLoaded:false, errors:[]}
},
componentDidMount: function() {
if(this.props.initialLoad) { this.props.initialLoad(); }
if(this.props.changeListener) { this.props.changeListener(this.onChange); }
},
onChange: function() {
let res = this.props.loadData();
this.setState({errors: res.errors, isLoaded: this.props.isLoadedCheck(res)});
},
render: function() {
if(!this.state.isLoaded) {
let errors = this.state.errors && (<div>{this.state.errors.length} errors</div>)
return (<div>{errors}<LoadingGraphic /> </div>)
}
return <div>{this.props.children}</div>
}
});
const Wrapper = React.createClass({
getDefaultProps: function() {
return {id:23}
},
render: function() {
let initialLoad = () => someActionCreator.getData(this.props.id);
let loadData = () => someStore.getData(this.props.id);
let changeListener = (fn) => someStore.onChange(fn);
return (<div><LoadingContainer initialLoad={initialLoad}
changeListener={changeListener}
loadData={loadData}
isLoadedCheck={(res) => res.someData != null}><SomeComponent id={this.props.id} /></LoadingContainer></div>)
}
});
while it adds another stateless wrapper, it gives a clean way to make sure your components dont just load on mount and a common place to show api feedback etc.
and with react 14 these sort of pure stateless wrappers are getting a bit of a push, with perf improvements to come, so we've found it scales nicely
This is the pattern which will help you in getting your individual components to manage user interactions
var MyComponent = React.createClass({
getInitialState: function() {
return {
item: [],
loading: true,
};
},
componentDidMount: function() {
//Make your API calls here
var self = this;
$.ajax({
method: 'GET',
url: 'http://jsonplaceholder.typicode.com/posts/1',
success: function(data) {
if (self.isMounted()) {
self.setState({
item: data,
loading: false
});
}
}
});
},
render: function() {
var componentContent = null;
if (this.state.loading) {
componentContent = (<div className="loader"></div>);
} else {
componentContent = (
<div>
<h4>{this.state.item.title}</h4>
<p>{this.state.item.body}</p>
</div>
);
}
return componentContent;
}});