How to use setState in ReactJS? - reactjs

In the follow code, I thought the text should update to the new one after 3 seconds:
https://jsfiddle.net/smrfcr9x/1/
var Component = React.createClass({
render: function() {
return React.DOM.span(null, "hello " + this.props.text);
}
});
var aComponent = React.render(
React.createElement(Component, {
text: "abcd"
}),
document.getElementById("app")
);
setTimeout(function() {
console.log("now setting state");
aComponent.setState({
text: "lmno"
});
}, 3000);
How is it actually done?

You should use setProps to replace setState.
Please notice the warning in console from react.js:
Warning: setProps(...) and replaceProps(...) are deprecated. Instead, call render again at the top level.
You may follow that warning.
EDIT:
Sorry for haven't said it clearly just now.
Using setProps to replace setState can do correctly what you want. But the method setProps has been deprecated and will show the warning above. So I think you should follow the waring, once you want to change the props, rerender the component.
You read the text from props not state in the component, so it won't work if you set the state.
EDIT2:
var Component = React.createClass({
render: function() {
return React.DOM.span(null, "hello " + this.props.text);
}
});
var renderComponent = function(text){
React.render(
React.createElement(Component, {
text: text
}),
document.getElementById("app")
);
}
renderComponent('abcd')
setTimeout(function() {
console.log("now setting state");
renderComponent('lmno')
}, 3000);
I'm sorry for not knowing how to share code in jsfiddle. This code work fine in it.

Ok, I think I get it. The code to needs to set a state, and in render, use this.state instead of this.props:
https://jsfiddle.net/smrfcr9x/7/
var Component = React.createClass({
getInitialState: function() {
return {
text: this.props.text,
};
},
render: function() {
return React.DOM.span(null, "hello " + this.state.text);
}
});

Related

setState in react is not working in react component

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)
});

React Native - Call Function of child from NavigatorIOS

I am trying to call a child function from the right button on the parent navigator.
A basic code example of what I need is as follows:
Main.js
<NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Test',
component: Test,
rightButtonTitle: 'Change String',
onRightButtonPress: () => ***I Want to call miscFunction from here*** ,
passProps: {
FBId: this.state.fbidProp,
favsPage: true
}
}}/>
Test.js
class Test extends React.Component{
constructor(props){
super(props);
this.state = {
variable: 'some string'
};
}
miscFunction(){
this.setState({
variable: 'new string'
};
}
render(){
return(
<Text> {variable} </Text>
)
}
}
This is covered in the following github issue:
https://github.com/facebook/react-native/issues/31
Eric Vicenti comments to describe how Facebook solves this internally:
Currently the best way to do that is to create an EventEmitter in the owner of the NavigatorIOS, then you can pass it down to children using route.passProps. The child can mix in Subscribable.Mixin and then in componentDidMount, you can
this.addListenerOn(this.props.events, 'myRightBtnEvent', this._handleRightBtnPress);
It is clear that this API needs improvement. We are actively working the routing API in Relay, and hopefully react-router, but we want NavigatorIOS to be usable independently. Maybe we should add an event emitter inside the navigator object, so child components can subscribe to various navigator activity:
this.addListenerOn(this.props.navigator.events, 'rightButtonPress', this._handleRightBtnPress);
Here's how this looks in a practical example:
'use strict';
var React = require('react-native');
var EventEmitter = require('EventEmitter');
var Subscribable = require('Subscribable');
var {
AppRegistry,
StyleSheet,
Text,
View,
NavigatorIOS
} = React;
First we pull in all of our requirements including the EventEmitter and Subscribable.
var App = React.createClass({
componentWillMount: function() {
this.eventEmitter = new EventEmitter();
},
onRightButtonPress: function() {
this.eventEmitter.emit('myRightBtnEvent', { someArg: 'argValue' });
},
render: function() {
return <NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Test',
component: Test,
rightButtonTitle: 'Change String',
onRightButtonPress: this.onRightButtonPress,
passProps: {
events: this.eventEmitter
}
}}/>
}
});
In our main top-level component, we create a new EventEmitter (in componentWillMount) to be available across the component, and then use passProps to pass it down to the Test component we specify for the navigator.
We also define a handler for the right button press, which emits a myRightBtnEvent with some dummy arguments when that button is pressed. Now, in the Test component:
var Test = React.createClass({
mixins: [Subscribable.Mixin],
getInitialState: function() {
return {
variable: 'original string'
};
},
componentDidMount: function() {
this.addListenerOn(this.props.events, 'myRightBtnEvent', this.miscFunction);
},
miscFunction: function(args){
this.setState({
variable: args.someArg
});
},
render: function(){
return(
<View style={styles.scene}><Text>{this.state.variable}</Text></View>
)
}
});
We add the Subscribable mixin, and the only other thing we need to do is listen out for the myRightBtnEvent being fired from the App component and hook miscFunction up to it. The miscFunction will be passed the dummy arguments from the App press handler so we can use those to set state or perform other actions.
You can see a working version of this on RNPlay:
https://rnplay.org/apps/H5mMNQ
A. In initial component
this.props.navigator.push({
title: 'title',
component: MyComponent,
rightButtonTitle: 'rightButton',
passProps: {
ref: (component) => {this.pushedComponent = component},
},
onRightButtonPress: () => {
// call func
this.pushedComponent && this.pushedComponent.myFunc();
},
});
B. In pushed component
replace onRightButtonPress func in pushed component.
componentDidMount: function() {
// get current route
var route = this.props.navigator.navigationContext.currentRoute;
// update onRightButtonPress func
route.onRightButtonPress = () => {
// call func in pushed component
this.myFunc();
};
// component will not rerender
this.props.navigator.replace(route);
},
You can use the flux, here is a demo: https://github.com/backslash112/react-native_flux_demo

How to pass function groups as props in React?

I use this simple React component only for example.
I would like to access this.setState() inside the functions 'working' and 'group.notWorking'.
var myComponent = React.createClass({
getInitialState: function() {
return {};
},
working: function() {
this.setState({ test: true }); //this is myComponent
},
group: {
notWorking: function() {
console.log(this); //this is window
}
},
render: function() {
return (
<div>
<ChildComponent working={this.working} group={this.group}/>
</div>
);
},
});
My question is how do you pass functions grouped in an object, or is there any best practice, to avoid passing all the functions one by one to children components.
You need to pass a bound version of it.
<ChildComponent working={this.working} group={this.group.notWorking.bind(this)}/>
If you want to pass the whole group you need to make it a function which returns an object and bind it:
group: function() {
return {
notWorking: function() {
console.log(this);
}.bind(this)
};
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

ReactJS and Reflux listening to a store before component mounts

I have this doubt that I haven't been able to google out yet but I have this react component that I want to update it's state using a reflux store using componentWillMount() method.
I am able to update the state in the store but using this.trigger to update it's state from the store didn't give me the updated state of the data which got me confused. How can I get the updated state of the data.
Here is what my component is like at the moment
var Challenges = React.createClass({
contextTypes: {
router: React.PropTypes.func
},
mixins: [Reflux.connect(ChallengeStore,'challenges')],
getInitialState: function() {
return {
challenges: []
}
}
componentDidMount: function() {
var trackId = this.props.params.trackId; // the url
ChallengeActions.GetChallenges(trackId);
console.log(this.state);
},
render: function () {
return(
<div>
<h1>{ this.state.challenges.title }</h1> <List challenges={ this.state.challenges } />
</div>
);
}
});
And my store here
var ChallengeStore = Reflux.createStore({
listenables: ChallengeActions,
onGetChallenges: function(url) {
var items = ChallengeService.getChallenges(url);
this.trigger({
challenges: items
});
}
});
Ran into this while figuring out Reflux this week.
The issue is Reflux.connect only connects a getInitialState() in the store which your store seems is missing.
As per the docs:
The Reflux.connect() mixin will check the store for a getInitialState
method. If found it will set the components getInitialState
Unless your store's initial state is consistent across all it's listeners, I find it's better to just use Reflux.listenTo():
var Status = React.createClass({
mixins: [Reflux.listenTo(statusStore,"onStatusChange")],
onStatusChange: function(status) {
this.setState({
currentStatus: status
});
},
render: function() {
// render using `this.state.currentStatus`
}
});

how to use setProps in react.js

I want to call setProps from outside of myComponent to be able to dynamically change data for myComponent.
I expect that after changing props of the component, it will re-render itself.
I was trying the following:
var myComponent = React.createClass({
render: function () {
return (
React.DOM.div(null, this.props.data)
);
}
});
React.renderComponent(
myComponent({ data: someData }),
document.getElementById('predictionContent')
);
myComponent.setProps({data: someData2});
The problem is that I don't understand how to use setProps for the component.
In my case, I receive "undefined" error.
How to solve this?
warning: setProps is now deprecated.
React.createClass returns a class, it is React.renderComponent which returns an instance of the class which has the setProps method.
Try this instead:
var MyComponent = React.createClass({
render: function () {
return React.DOM.div(null, this.props.data);
}
});
var myComponent = React.renderComponent(
new MyComponent({ data: someData }),
document.getElementById('predictionContent')
);
myComponent.setProps({ data: someData2 });
Having said that, Chad Scira's answer is also correct: probably better to re-render than to call setProps. That will keep it looking the same as the code inside the render() method, and you'll always be able to call renderComponent, regardless of whether it is the first time or a subsequent update.
Like this:
var MyComponent = React.createClass({
render: function () {
return React.DOM.div(null, this.props.data);
}
});
React.renderComponent(
new MyComponent({ data: someData }),
document.getElementById('predictionContent')
);
// later
React.renderComponent(
new MyComponent({ data: someData2 }),
document.getElementById('predictionContent')
);
You're not supposed to do that. Instead just run the renderComponent method again like this:
React.renderComponent(
myComponent({ data: someData2 }),
document.getElementById('predictionContent')
);
react will automatically resolve the differences

Resources