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.
Related
First time data loads properly but when i click filter button like latest or top ajax is passing but view not getting updated. I am not sure what is wrong in my code. I am new to react js.
Here is my example code :-
import React from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import css from './css/bootstrap.css';
//import Search from './Search';
class FetchDemo extends React.Component {
constructor(props) {
super(props);
this.state = {
posts: [],
loading: true,
error: null
};
}
componentDidMount() {
// Remove the 'www.' to cause a CORS error (and see the error state)
axios.get(`https://newsapi.org/v1/articles?source=techcrunch&apiKey=789ea3cd651a49e5ba9fc2061d68138f`)
.then(res => {
//console.log(res.data);
// Transform the raw data by extracting the nested posts
const posts = res.data.articles;
//console.log(posts);
// Update state to trigger a re-render.
// Clear any errors, and turn off the loading indiciator.
this.setState({
posts,
loading: false,
error: null
});
//console.log(this.setState);
})
.catch(err => {
// Something went wrong. Save the error in state and re-render.
this.setState({
loading: false,
error: err
});
});
}
renderLoading() {
return <div>Loading...</div>;
}
renderError() {
return (
<div>
Uh oh: {this.state.error.message}
</div>
);
}
renderPosts() {
if(this.state.error) {
return this.renderError();
}
return (
<div className="row">
<First1/>
{this.state.posts.map(post =>
<div className="col-md-3">
<img src={post.urlToImage} className="img-responsive" />
<h2 key={post.id}>{post.title}</h2>
<p className="lead">
by {post.author}
</p>
<p><span className="glyphicon glyphicon-time"></span> Posted on {post.publishedAt}</p>
<p>{post.description}</p>
</div>
)}
</div>
);
}
render() {
return (
<div>
<h1>Top Stories</h1>
{this.state.loading ?
this.renderLoading()
: this.renderPosts()}
</div>
);
}
}
var First1 = React.createClass({
myClick: function(e){
alert(e.currentTarget.getAttribute("data-city"));
var city = e.currentTarget.getAttribute("data-city");
//alert('Show 1');
axios.get('https://newsapi.org/v1/articles?source=techcrunch&&sortBy='+city+'&apiKey=789ea3cd651a49e5ba9fc2061d68138f')
.then(res => {
//console.log(res.data);
// Transform the raw data by extracting the nested posts
const posts = res.data.articles;
//console.log(posts);
// Update state to trigger a re-render.
// Clear any errors, and turn off the loading indiciator.
//console.log(posts);
this.setState({
posts,
loading: false,
error: null
});
//console.log(this.setState);
})
.catch(err => {
// Something went wrong. Save the error in state and re-render.
this.setState({
loading: false,
error: err
});
});
},
render: function() {
return (<div>
<a onClick={this.myClick} data-city="latest"> Latest</a>
<a onClick={this.myClick} data-city="top"> Top</a>
</div>
);
}
});
// Change the subreddit to anything you like
ReactDOM.render(
<FetchDemo subreddit="reactjs"/>,
document.getElementById('root')
);
Here is link https://jsfiddle.net/69z2wepo/74393/
Issue is first time you are setting the data in parent component, and second time setting the data in child component, you need to update the state of parent component on click of top and latest.
Solution:
Pass a function from parent component and use that function to update the state once you get the response in child component, like this:
In Parent Component:
<First1 _updateState={this._updateState.bind(this)}/>
_updateState(posts){
this.setState({
posts,
loading: false,
error: null
});
}
In Child Component:
myClick: function(e){
....
.then(res => {
this.props._updateState(res.data.articles) //pass data to parent component
})
....
},
Check the fiddle for working solution: https://jsfiddle.net/ndg24fqc/
Note: In 1st component you are using es6 and in 2nd component you are using es5, try to use one thing either es6 or es5.
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>
I'm using react with react-router. After checking authentication with onEnter Asynchronous hook on IndexRoute, App component gets rendered. App component has an initial state auth which is set to undefined when it renders. auth state is being passed to Navbar component as prop where it will be used to decide whether or not to show login, register and logout links.
When App component is done rendering, componentDidMount() makes an ajax call to again check if user is authenticated. On response it makes change to the state. After state change from ajax request, i'm logging state to console, this.setState() method is not changing state but somehow still triggers componentWillReceiveProps() method on Navbar component and this.props.auth value is still undefined.
// Checks Authentication Asynchronously
isAuthenticated(nextState, replace, callback) {
$.ajax({
type : 'GET',
url : '/auth',
success : function(res){
if(!res){
callback(replace({ pathname: '/login', query: { auth: 'false' } }));
}else{
callback();
}
}
});
};
// routes
var routes = (
<Router history={browserHistory}>
<Route path="/" component={require('./components/app')}>
<IndexRoute component={require('./components/dashboard/index')} onEnter={Auth.isAuthenticated}/>
<Route path="/register"
component={require('./components/authentication/register')}
onEnter={Auth.isNotAuthenticated} />
<Route path="/login"
component={require('./components/authentication/login')}
onEnter={Auth.isNotAuthenticated}/>
<Route path="*"
component={require('./components/404/404')}/>
</Route>
</Router>
);
// App
const App = React.createClass({
getInitialState(){
return {
auth : undefined
}
},
componentDidMount(){
console.log('App componentDidMount');
this.checkAuth();
},
checkAuth(){
var self = this;
$.ajax({
type : 'GET',
url : '/auth',
success : function(res){
if(res){
self.setState({
auth : true
});
}else{
self.setState({ auth : false});
}
}
});
console.log(this.state.auth);
},
render() {
return(
<div className="appWrapper">
<Navbar auth={this.state.auth}/>
<div className="container">
{this.props.children}
</div>
</div>
);
}
});
// Navbar
var Navbar = React.createClass({
getInitialState(){
return{
user_actions : '' ,
auth : this.props.auth
}
},
componentDidMount(){
console.log('Navbar componentDidMount ', this.props.auth);
this.checkAuthState();
},
componentWillReceiveProps(){
console.log('Navbar componentWillReceiveProps ', this.props.auth);
this.setState({
auth : this.props.auth
});
this.checkAuthState();
},
checkAuthState(){
console.log('Nav Mounted with auth : ', this.state.auth);
if(this.state.auth == undefined){
this.state.user_actions = '';
}
if(!this.state.auth){
this.state.user_actions = <ul className="nav navbar-nav navbar-right">
<li>Login</li>
<li>Register</li>
</ul>;
this.setState({
user_actions : this.state.user_actions
});
}
if(this.state.auth){
this.state.user_actions = <ul className="nav navbar-nav navbar-right">
<li>Logout</li>
</ul>;
this.setState({
user_actions : this.state.user_actions
});
}
},
render : function(){
return (
<nav className="navbar navbar-default">
<div className="container">
Reactor
{this.state.user_actions}
</div>
</nav>
);
}
});
First of all, I suggest you to reread React.JS documentation, because there are couple of things that need to be noted:
Never mutate this.state directly, use setState method instead. (line: 108, 111, 121, 133, 136, 146)
You should use state for storing data that changes over time, not an element. (line: 111, 121, 136, 146)
tl;dr;
Let's go back to the questions:
1. Ajax response is changing a state value, but the value isn't change in your log.
You won't see it if you print the value after ajax request! The reason is:
First, you're doing asynchronous request using Ajax and trying to see the result in synchronous way. JS will execute your console.log first which still contains the value before request, and then perform ajax request callback. This is the block of your code:
$.ajax({ ...,
success: function(res) {
if(res) { self.setState({ auth : true }); }/
...
} // will executed later (after ajax get response)
});
console.log(this.state.auth); // will executed first, this is why it always prints the value as undefined
Second, you won't able to see the changed state value right after you set a new state value. For instance, let say the value of this.state.auth is false:
this.setState({ auth: true});
console.log(this.state.auth); // will print false, instead of true as your new value
You're able to see your new state value by using componentWillUpdate(nextProps, nextState) method. You can read about this from this link: React.JS Component Specs and Lifecycle
2. Still triggers componentWillReceiveProps() method on Navbar component and this.props.auth value is still undefined.
It means that your state value is successfully changed by setState() on your ajax response. The proof is Navbar component receive a new props that send it down by App component (where the auth state is changed) which will trigger componentWillReceiveProps() method.
Maybe your code should be like this:
// App
const App = React.createClass({
getInitialState : function(){
return {
auth : false
}
},
componentDidMount : function() {
console.log('App componentDidMount');
this.checkAuth();
},
componentWillUpdate : function(nextProps, nextState) {
//you'll see the changing state value in here
console.log('Your prev auth state: ' + this.state.auth);
console.log('Your next auth state: ' + nextState.auth);
},
checkAuth : function(){
var self = this;
$.ajax({
type : 'GET',
url : '/auth',
success : function(res){
if(res){
self.setState({ auth : true });
}
}
});
},
render : function(){
return(
<div className="appWrapper">
<Navbar auth={this.state.auth}/>
<div className="container">
{this.props.children}
</div>
</div>
);
}
});
// Navbar
// Because the navbar component receive data (this.props.auth) from parent (app) via props, so we're no longer need to assign auth as a state in Navbar component.
const Navbar = React.createClass({
render : function(){
// you're no longer need checkAuthState method
let navItems;
if(!this.props.auth){
navItems = (<ul className="nav navbar-nav navbar-right">
<li>Login</li>
<li>Register</li>
</ul>);
} else {
navItems = (<ul className="nav navbar-nav navbar-right">
<li>Logout</li>
</ul>);
}
return (
<nav className="navbar navbar-default">
<div className="container">
Reactor
{ navItems }
</div>
</nav>
);
}
});
Hope it helps!
In ajax scope. it is unable to access react state. as a alternative you can call other method in the module as ajax success call, then update state there.
follow this example.
var reactModule = React.createClass({
getInitialState:function(){
},
render: function() {
return (
<div>
content
</div>
);
},
componentDidMount: function() {
var ajaxSuccess=this.ajaxSuccess;
$.ajax({
type: "POST",
url: $api_url + 'index.php/app/icon1_update',
dataType: "text",
data:fd,
contentType: false,
processData: false,
success: ajaxSuccess
});
},
ajaxSuccess:function(e){
//e is the result. update state here.
}
});
Just use arrow functions to access ‘this’:
success: () => {
this.setState({ data: value })
}
please check the Documentation of componentWillReceiveProps:
componentWillReceiveProps(
object nextProps
)
https://facebook.github.io/react/docs/component-specs.html#updating-componentwillreceiveprops
When your properties will change, then access to the properties "nextProps". Otherwise you will access to the old properties.
As a small hint:
Include your checkAuthState() code in the render method, not in the componentDidMount method because therefore you can avoid your setState call.
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.