if(this.isMounted()) not being called? - reactjs

Can you tell me why when I do this:
var SomeComponent = React.createClass({
getData: function(){
if (this.isMounted()){
var queryInfo = {
userId: sessionStorage.getItem("user_id"),
userRole: sessionStorage.getItem('user_role'),
aptId : this.props.params
}
io = io.connect();
io.emit('allTasks', queryInfo);
io.on('allTasksInfo', function(data){
reqwest({
url: '/apartment/tasks/address',
method: 'get',
xhrFields: {withCredentials: true},
crossOrigin: true
}).then(function(data){
this.setState({
dataSet: arr
})
}.bind(this));
}.bind(this));
}
},
componentDidMount: function(){
this.getData();
},
render: function(){...}
});
The code inside the if is executed, but I get the Uncaught 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.
But when I do this:
var SomeComponent = React.createClass({
getData: function(){
var queryInfo = {
userId: sessionStorage.getItem("user_id"),
userRole: sessionStorage.getItem('user_role'),
aptId : location.pathname.split("/")[4]
}
reqwest({
url:'/operation/staff',
method: 'get',
xhrFields: {withCredentials: true},
crossOrigin: true
}).then(function(data){
if(this.isMounted()){
this.setState({
operationStaff: data
})
}
}.bind(this));
}
componentDidMount: function(){
this.getData();
},
render: function(){...}
});
Everything is ok. Shouldn't the first just be executed when the component is mounted? What I am missing?
EDIT: I'm using react-router and express server with socket.io with server rendering (just the components, not the data - this I will fetch client side). After the answers, I can say:
The component is not unmounting
I can now tell that at first render, this warning doesn't appear even on second example:
https://drive.google.com/file/d/0B1rbX9C6kejlbWVKeTZ6WVdGN0E/view?usp=sharing
But if I change the url and get back to this path (and here yes, the component unmounts off course), the Ajax reqwest is being called 2 times
https://drive.google.com/file/d/0B1rbX9C6kejlUjFRYTBtejVLZGs/view?usp=sharing
This has something to do with the sockets implementation.
I will close this issue and open another regarding this. Thank you for the help.

Shouldn't the first just be executed when the component is mounted?
Yes, and it is (what makes you think it is not?).
However, the Ajax callback itself is executed some time in the future and at that moment, the component may already be unmounted.
In the first example, the test is useless since the component is always mounted after componentDidMount was called. In the second example, you are testing whether the component is mounted just before you call setState, which makes more sense.
Here is a simplified example:
var Hello = React.createClass({
getInitialState: function() {
return {name: 'foo'};
},
componentDidMount: function() {
console.log('mounted...');
setTimeout(function() {
// this works fine
console.log('updating state once...');
this.setState({
name: 'bar'
});
}.bind(this), 1000);
setTimeout(function() {
// this will throw
console.log('updating state twice...');
this.setState({
name: 'baz'
});
}.bind(this), 3000);
},
componentWillUnmount: function() {
console.log('unmounting...');
},
render: function() {
return <div>Hello {this.state.name}</div>;
}
});
React.render(
<Hello />,
document.getElementById('container')
);
setTimeout(function() {
React.unmountComponentAtNode(
document.getElementById('container')
);
}, 2000);
If you run it you will notice that the second timeout will generate the same error because it is called after the component was unmounted:
Console output:
mounted...
updating state once...
unmounting...
updating state twice...
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.
DEMO: https://jsfiddle.net/pkzfbcr5/

I'm using react-router and express server with socket.io with server rendering (just the components, not the data - this I will fetch client side). After the answers, I can say:
The component is not unmounting
I can now tell that at first render, this warning doesn't appear even on second example: https://drive.google.com/file/d/0B1rbX9C6kejlbWVKeTZ6WVdGN0E/view?usp=sharing
But if I change the url and get back to this path (and here yes, the component unmounts off course), the Ajax reqwest is being called 2 times https://drive.google.com/file/d/0B1rbX9C6kejlUjFRYTBtejVLZGs/view?usp=sharing
This has something to do with the sockets implementation.
I will close this issue and open another regarding this. Thank you for the help.

Related

Make AJAX request when the property was changed

I would like to have a component, which get the property from parent component and make an AJAX request, based on this propery. The parent component can change this property and my child component must get another one AJAX request.
Here is my code, but I am not sure it is optimal and even correct:
<News source={this.state.currentSource} />
Component:
var News = React.createClass({
propTypes: {
source: React.PropTypes.string
},
getInitialState: function() {
return {
entities: []
};
},
componentWillReceiveProps(nextProps) {
var url = 'http://localhost:3000/api/sources/' + nextProps.source + '/news';
this.serverRequest = $.get(url, function(result) {
this.setState({
entities: result
});
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
// ...
}});
module.exports = News;
Does componentWillReceiveProps is a good place for this code?
componentWillReceiveProps will work just fine, one thing I would do differently is if <News/> is getting its props from a parent component then a good pattern to follow is to have the parent component actually handle the api calls and pass down the results as props. This way your News component really only has to deal with rendering content based on its props vs rendering and managing state.
I can only see limited portion of your App so that might not fit your use case but here is a great article on doing that type of differentiation between smart and dumb components.
http://jaketrent.com/post/smart-dumb-components-react/

Making ajax call in Reactjs, value coming empty while rendering

I am new to React.js and trying to render the result from an ajax call using react but the data is not coming to the render() function. If you know any other best way to solve this issue or make an ajax call in react then please let mention the link for the same. Need to render data in react from an API.Below is the javascript for the same. The value is coming in componentDidMount method and result is having the value. But when I try to access in the render, then its empty. I tried to assign value to a global object array but that is also not working. Any solutions for this.The link mentioned in the post for fetching data is a working link. You can call that link in the browser and check for json field.
var UserGist = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
};
},
componentDidMount: function() {
this.serverRequest = $.get(this.props.source, function(result) {
this.setState({
username = result.description,
lastGistUrl = result.html_url
})
}.bind(this));
},
componentWillUnmount: function() {
this.serverRequest.abort();
},
render: function() {
React.createElement("div", null, this.state.username, " 's last gist's url is ",
React.createElement("div", null, this.state.lastGistUrl, ""), ".")
}
});
ReactDOM.render(React.createElement(UserGist, { source: "https://api.github.com/users/octocat/gists" }), document.getElementById('container'));
Fiddler link
Your code contains syntax errors. Replace = with :.
componentDidMount: function() {
this.serverRequest = $.get(this.props.source, function(result) {
this.setState({
username: result.description,
lastGistUrl: result.html_url
})
}.bind(this));
},
More errors to come:
render missing return statement. Should be return React.createElement
result is an array not an object. You need to somehow handle this. For example pick the first element result = result[0]
setState is method. You should call it not make an assignment setState=({}) should be setState({}) This one was in demo code.
See fixed demo.

Understanding React props

Looking at Facebook's react example here, I found this code showing how to use mixins to set intervals. I am confused as to what is happening with this.intervals. I understand that state holds render-altering data, and props handle data handed down from a parent component, ideally. I would have used this.props.intervals instead, but what is the difference between the two?
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
var TickTock = React.createClass({
mixins: [SetIntervalMixin], // Use the mixin
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000); // Call a method on the mixin
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
React has been running for {this.state.seconds} seconds.
</p>
);
}
});
ReactDOM.render(
<TickTock />,
document.getElementById('example')
);
When you use props, you know for 100% certainty the value should will be coming from it's immediate parent component (as a property).
When you see state, you know the value is being born/created within that component it's self.
The key, when state changes, every child below will render if any of their received props change.
Your Mixin is not a normal React class. It is simply an object, so this in the case of this.interval, is a reference to the scope of the object in which the method is being executed - TickTock.

ReactJS - Need to click twice to set State and run function

Here is a summary of the code I have inside my React component:
getInitialState: function(){
return{link:""}
},
onClick1: function(){
this.setState({link:"Link1"});
this.otherFunction();
},
onClick2: function(){
this.setState({link:"Link2"});
this.otherFunction();
},
otherFunction:function(){
//API call to this.state.link
},
render: function(){
return <div>
<button1 onClick={this.onClick1}>Link1</button>
<button2 onClick={this.onClick2}>Link2</button>
//...some code to display the results of API call
</div>
}
The problem I have is that the first time I click the button, the otherFunction will run, but it will not have the updated value of myState. If I click a second time, then it works correctly.
From the docs:
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.
If you want a function to execute after the state transition completes, pass it in as a callback:
onClick1: function() {
this.setState({link:"Link1"}, this.otherFunction);
},
Well, here I am answering my own question, for future reference.
I figured it out. I removed this.otherFunction() from the onClick functions, and put it in componentWillUpdate. So it looks like this:
getInitialState: function(){
return{link:""}
},
onClick1: function(){
this.setState({link:"Link1"});
},
onClick2: function(){
this.setState({link:"Link2"});
},
otherFunction:function(){
//API call to this.state.link
},
componentWillUpdate(){
this.otherFunction();
},
render: function(){
return <div>
<button1 onClick={this.onClick1}>Link1</button>
<button2 onClick={this.onClick2}>Link2</button>
//...some code to display the results of API call
</div>
}
If all that onClick does is change the state, then you shouldn't have two functions that do the same job. You should have the new value of the "link" state passed as an argument to the function "onClick" :)

How to freely use pojo's, having setters, as state in React

Hitting a roadbump, I’m seeking some help. I'm starting to move more of my "state" pojo's out of my React components, due to for example being unsure how how my pojo’s setter methods should be utilized now (one may want setter methods to validate, etc.). I now find myself either defying React docs' warning to NEVER touch this.state directly or moving most code except rendering – including state - outside of the React component into my own js variables/objects (and holding a reference to the rendered object then using forceUpdate() to rerender). What is the recommended way to freely use whatever plain old js data/model objects I want, including with setter methods?
This barebones example, where I’m wanting a form-backing data object, demonstrates this difference I’m facing: http://jsfiddle.net/jL0rf0ed/ vs. http://jsfiddle.net/rzuswg9x/. Also pasted the code for the first below.
At the very least, I have this specific question: following a custom/manual update of this.state, does a this.setState(this.state) line, which would be from within the React component, and a component.forceUpdate() line, which would likely be from outside the React component, work just as fast and correctly as the standard this.setState({someKey: someValue})?
Thanks.
//props: dataObj, handleInputChange
test.ComponentA = React.createClass({
getInitialState: function() {
return {
age: 21,
email: 'a#b.com', //TODO make members private
setEmail: function(val) { //TODO utilize this
this.email = val;
if(val.indexOf('#') == -1) {
//TODO set or report an error
}
}
}
},
handleInputChange: function(e) {
this.state[e.target.name]=e.target.value; //defying the "NEVER touch this.state" warning (but it appears to work fine)!
this.setState(this.state); //and then this strange line
},
render: function() {
return (
<div>
<input type='text' name='age' onChange={this.handleInputChange} value={this.state.age}></input>
<input type='text' name='email' onChange={this.handleInputChange} value={this.state.email}></input>
<div>{JSON.stringify(this.state)}</div>
</div>
);
}
});
React.render(<test.ComponentA />, document.body);
For your code example in your pasted snippet, you can do the following.
handleInputChange: function(e) {
var updates = {};
updates[e.target.name] = e.target.value;
this.setState(updates);
},
In your second example, you should never call forceUpdate or setState from outside the component itself. The correct way would be for the state to be contained in whatever renders your component and pass in the data as props.
Usually this means you have a wrapper component.
var RootComponent = React.createClass({
getInitialState: ...
onInputChange: function() {
this.setState({yourKey: yourValue});
},
render: function() {
return <SubComponent yourKey={this.state.yourKey} onInputChange={this.onInputChange} />;
}
};
In your case, I would recommend creating this wrapper component. Another solution is just to rerender the same component into the same DOM node.
test.handleInputChange = function(e) {
// update test.formPojo1 here
React.render(<test.ComponentA dataObj={test.formPojo1} handleInputChange={...} />);
}
Because it is the same component class and DOM node, React will treat it as an update.
Stores
Facebook uses the concept of a Store in their Flux architecture.
Stores are a very targeted POJO. And I find that it is pretty simple to use the Store metaphors without the using the entirety of Flux.
Sample Store
This is a Store that I pulled out of one of our production React apps:
ChatMessageStore = {
chatMessages: [],
callbacks: [],
getAll: function() {
return this.chatMessages;
},
init: function() {
this.chatMessages = window.chat_messages.slice();
return this.emitChange();
},
create: function(message) {
this.chatMessages.push(message);
return this.emitChange();
},
emitChange: function() {
return this.callbacks.forEach(callback, function() {
return callback();
});
},
addChangeListener: function(callback) {
return this.callbacks.push(callback);
},
removeChangeListener: function(callback) {
return this.callbacks = _.without(this.callbacks, callback);
}
};
Hooking it up to a React Component
In your component you can now query the store for its data:
var Chat = React.createClass({
componentWillMount: function() {
return ChatMessageStore.addChangeListener(this._onChange);
},
componentWillUnmount: function() {
return ChatMessageStore.removeChangeListener(this._onChange);
},
getInitialState: function() {
return this.getMessageState();
},
getMessageState: function() {
return {
messages: ChatMessageStore.getAll()
};
}
});
The Component registers a callback with the Store, which is fired on every change, updating the component and obeying the law of "don't modify state."

Resources