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.
Related
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);
}
Hello Im creating a simple React Component with just a label that change its content when a SignalR method is fired. My react component is like this one:
var PersonalityStatusApp = React.createClass({
getInitialState: function () {
return { data: dataInit };
},
componentWillMount(){
var self = this;
this.setState({ data:this.props.status});
Votinghub.on("UpdateStatusLabel", function (data) {
var obj = $.parseJSON(data);
self.setState({ data: obj });
});
},
render: function () {
return (
<div className="PersonalityStatusApp">
<label>{this.props.status}</label>
</div>
);
}
});
When te component receives a UpdateStatusLabel signalR message it change the State of the component with the value that gets from the signalR message.
The method UpdateStatusLabel gets the correct value.
This fires the render method, but when I check the properties in the render method I see thnat the values are still the ones from the initial state.
Can somebody help me?
Reason is, you are updating the state variable and printing the props value. Initially state variable data will have the value of this.props and after you get the signalR you are updating the state by data: obj, so print the value of this.stat.data.status it will print the updated value.
use this:
return (
<div className="PersonalityStatusApp">
<label>{this.state.data.status}</label>
</div>
);
Note: Initially you need to set the value of data: this.props
Full part:
var PersonalityStatusApp = React.createClass({
getInitialState: function () {
return { data: dataInit };
},
componentWillMount(){
var self = this;
this.setState({ data: this.props}); //changed this
Votinghub.on("UpdateStatusLabel", function (data) {
var obj = $.parseJSON(data);
self.setState({ data: obj });
});
},
render: function () {
return (
<div className="PersonalityStatusApp">
<label>{this.state.data.status}</label>
</div>
);
}
});
I have developed the Component it's look like,
var ManageViewPage = React.createClass({
// Get initial state from stores
getInitialState: function() {
return setViewData();
},
componentDidMount: function() {
SystemMetaItemStore.CallSystemMetaItem("Views");
ItemStore.addChangeListener(this._onChange);
SystemMetaItemStore.addChangeListener(this._onChange);
alert("Did mount");
},
// Remove change listers from stores
componentWillUnmount: function() {
ItemStore.removeChangeListener(this._onChange);
SystemMetaItemStore.removeChangeListener(this._onChange);
alert("Did Unmount");
},
preventDefault: function (event) {
event.preventDefault();
},
DeleteViewclick:function(event)
{
},
render: function() {
return (
<div className="row">
</div>
)
},
_onChange: function() {
this.setState(setViewData());
}
});
module.exports =ManageViewPage;
When I will call this page first time using routing then it will call the alert "did mount" but when I press F5 to refresh the browser it will not called the alert "did mount" so can anyone guide me what I did wrong here ?
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;
}});
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!");
},