React state update step behind - reactjs

This color picker works but one step behind. I've been using React 15.4.2.
Is it an issue to be fixed in later versions?
In case it's my fault, please, how to master "pending state condition"?
Pen http://codepen.io/462960/pen/qrBLje
Code:
let Input = React.createClass({
getInitialState(){
return {
today_color: "#E5D380"
};
},
colorChange(e){
this.setState({
today_color: e.target.value
})
document.querySelector('html').style.setProperty('--base', this.state.today_color);
},
render(){
return (<div>
<input className="today_color" onChange={this.colorChange} type="color" defaultValue={this.state.today_color}/>
</div>)
}
})

The issue you are having is that once you call setState the component rerenders and this code isn't called again:
document.querySelector('html').style.setProperty('--base', this.state.today_color);
And the first time it is called, this.state.today_color is still the previous value.
What you should do is the following:
this.setState({
today_color: e.target.value
}, () => document.querySelector('html').style.setProperty('--base', this.state.today_color));
This makes sure the setProperty gets called after setState is done and after you have the correct value in your state.
Edit: here's a working example.

Related

Next and previous buttons not working properly

I am building a react app which renders differnet components on pressing the next and back buttons. The problem i am facing is that the both these buttons are reacting to the previous button press. Like if a first press next then nothing will happen. If then i press back then it is responding to the previous button press of next.
class Card extends Component {
constructor()
{
super();
this.state = {
button:"",
i:0
}
}
onClick = (event)=>{
this.setState({button: event.target.id})
if(this.state.button==="1")
{
this.setState({ i: this.state.i + 1 });
}
else if(this.state.button==="2")
{
this.setState({ i: this.state.i - 1 });
}
console.log(this.state.i)
}
render() {
return(
<div className="App">
<div >
<NavBar onButtonClick={this.onClick}/>
<CardList i={this.state.i} />
</div>
</div>
);
}
}
export default Card;
setState in React is batched by React, essentially it means, if you do 3 set state calls one after the other, they are all called sequentially.
I recommend reading the official docs for more details.
Now, on to your problem: you are setting a state based on the "previous value" in that state:
this.setState({ i: this.state.i - 1 });
This will cause "unexpected" behaviour due to the batched mode, as the this.state.i could've changed at the time it is actually called.
Here is what React docs recommend to do:
setState() does not always immediately update the component. It may
batch or defer the update until later. This makes reading this.state
right after calling setState() a potential pitfall. Instead, use
componentDidUpdate or a setState callback (setState(updater,
callback))
There is a second signature for setState method, which accepts a function with the signature: (state, props) => stateChange where props is optional, if you don't want to use it, no need to pass it.
The common mistake you seem to be doing is reading this.state after calling this.setState, this will not work.
The code below is not perfect, but hopefully shows, what you need to correct.
onClick = (event)=>{
var buttonId = event.target.id;
if(buttonId==="1")
{
this.setState((state) => {
return { i: state.i + 1, button: 1 }
});
}
else if(buttonId==="2")
{
this.setState((state) => {
return { i: state.i - 1, button: 2 }
});
}
}
Additional reading: If you still need to understand how to use functions in setState, I can recommend this article: https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b
this.setState takes a little bit of time to update the entire state, so you cannot reliably execute this.setState({button: event.target.id}) and then immediately afterward check the this.state.button - it's likely that the state hasn't finished being set yet.
Instead, I'd recommend setting your conditional based on the event.target.id, like this:
if (event.target.id === "1") {
this.setState({ i: this.state.i + 1 });
} else if (event.target.id === "2") {
this.setState({ i: this.state.i - 1 });
}
I would wait until the top of your render method to console.log your state for debugging purposes, as it will have completed being updated by then.
For more information on how to use setState properly, you can see this article, especially the section entitled "State Updates May Be Asynchronous".

Why does the newer 'setState' method not recognize my event.target?

Is the newer setState syntax not recommended for use with events?
While debugging, I saw that the first letter I typed came through with
a proper e.target.value.
Immediately afterwards though, I got the TypeError you see below.
// onChange(e) {
// this.setState(prevState => {
// return { username: e.target.value} <--- TypeError: Cannot read property 'value' of null
// })
// }
// obviously the setState below works fine
onChange(e) {
this.setState({ username: e.target.value});
}
<input type="text"
placeholder="What is your username?"
onChange={this.onChange}
// onChange={e => this.onChange(e)} - also tried this
value={this.state.username}/>
You are using the event in an asynchronous way by accessing it in the callback function you passed to setState(). By the time react calls your updater function all properties on the event have already been cleared.
From the react docs:
The SyntheticEvent is pooled. This means that the SyntheticEvent
object will be reused and all properties will be nullified after the
event callback has been invoked. This is for performance reasons. As
such, you cannot access the event in an asynchronous way.
To avoid that you either need to persist the event by calling event.perist(). This will enable you to use it later.
Or you assign the value you are interested in to a local variable that can be used asynchronously:
onChange(e) {
const username = e.target.value;
this.setState(prevState => ({username}));
}
Actually in your specific example you do not need the callback based version of setState() at all as you aren't updating the state based on previous state or props.
Update
I missed the error in the first place which is e.target is being null here. So, the real problem is not extracting the value from the event or using event.persist() there as #trixn explained in his/her answer. But, my answer includes this step though. So, it worked :)
Neither of them works if you don't bind your onChange function for this. Here is the working version of your code that you say does not work:
class App extends React.Component {
state = { username: "" };
onChange = (e) => {
const { value } = e.target;
this.setState(prevState => {
return { username: value}
})
}
render() {
return (
<div>
<input type="text"
placeholder="What is your username?"
onChange={this.onChange}
value={this.state.username} />
<p>Username: {this.state.username}</p>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I just used an arrow function instead of implicitly binding it. So it is bond automatically. Also, I extracted the value from e.target for React's synthetic events handling. We can't use events properly if we use them in a callback function like in setState's here. This is why I extracted it then use as a separate variable.

console not showing updated values after setState [duplicate]

Ok, i'll try and make this quick because it SHOULD be an easy fix...
I've read a bunch of similar questions, and the answer seems to be quite obvious. Nothing I would ever have to look up in the first place! But... I am having an error that I cannot fathom how to fix or why its happening.
As follows:
class NightlifeTypes extends Component {
constructor(props) {
super(props);
this.state = {
barClubLounge: false,
seeTheTown: true,
eventsEntertainment: true,
familyFriendlyOnly: false
}
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange = (event) => {
if(event.target.className == "barClubLounge") {
this.setState({barClubLounge: event.target.checked});
console.log(event.target.checked)
console.log(this.state.barClubLounge)
}
}
render() {
return (
<input className="barClubLounge" type='checkbox' onChange={this.handleOnChange} checked={this.state.barClubLounge}/>
)
}
More code surrounds this but this is where my problem lies. Should work, right?
I've also tried this:
handleOnChange = (event) => {
if(event.target.className == "barClubLounge") {
this.setState({barClubLounge: !this.state.barClubLounge});
console.log(event.target.checked)
console.log(this.state.barClubLounge)
}
So I have those two console.log()'s, both should be the same. I'm literally setting the state to be the same as the event.target.checked in the line above it!
But it always returns the opposite of what it should.
Same goes for when I use !this.state.barClubLounge; If it starts false, on my first click it remains false, even though whether the checkbox is checked or not is based off of the state!!
It's a crazy paradox and I have no idea whats going on, please help!
Reason is setState is asynchronous, you can't expect the updated state value just after the setState, if you want to check the value use a callback method. Pass a method as callback that will be get executed after the setState complete its task.
Why setState is asynchronous ?
This is because setState alters the state and causes re rendering. This can be an expensive operation and making it synchronous might leave the browser unresponsive.
Thus the setState calls are asynchronous as well as batched for better UI experience and performance.
From Doc:
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.
Using callback method with setState:
To check the updated state value just after the setState, use a callback method like this:
setState({ key: value }, () => {
console.log('updated state value', this.state.key)
})
Check this:
class NightlifeTypes extends React.Component {
constructor(props) {
super(props);
this.state = {
barClubLounge: false,
seeTheTown: true,
eventsEntertainment: true,
familyFriendlyOnly: false
}
}
handleOnChange = (event) => { // Arrow function binds `this`
let value = event.target.checked;
if(event.target.className == "barClubLounge") {
this.setState({ barClubLounge: value}, () => { //here
console.log(value);
console.log(this.state.barClubLounge);
//both will print same value
});
}
}
render() {
return (
<input className="barClubLounge" type='checkbox' onChange={this.handleOnChange} checked={this.state.barClubLounge}/>
)
}
}
ReactDOM.render(<NightlifeTypes/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'/>
Since setState is a async function. That means after calling setState state variable does not immediately change. So if you want to perform other actions immediately after changing the state you should use callback method of setstate inside your setState update function.
handleOnChange = (event) => {
let inputState = event.target.checked;
if(event.target.className == "barClubLounge") {
this.setState({ barClubLounge: inputState}, () => { //here
console.log(this.state.barClubLounge);
//here you can call other functions which use this state
variable //
});
}
}
This is by-design due to performance considerations. setState in React is a function guaranteed to re-render Component, which is a costly CPU process. As such, its designers wanted to optimize by gathering multiple rendering actions into one, hence setState is asynchronous.

componentWillReceiveProps infinite loop?

I am new to react and I am wondering if this is normal or not. If I place a console.log in the componentWillReceiveProps method, it just infinitely logs information. I have a if (this.props !== nextProps) {} check right below the console.log so nothing actually fires off but the infinite loop concerned me as a beginner. Can someone please shed some light on if this is normal or not?
I will some of the important snippets from my code.
// From the parent component
constructor(props: any) {
super(props);
this._handleChanged = this._handleChanged.bind(this);
this._onSave = this._onSave.bind(this);
this.state = { columns: {}, fieldsWithErrors: {}, loaded: false, loadedValues: {} };
}
componentDidMount() {
RestUtil.get().then((response) => {
// put in an if statement to check if response came back null
this.state.loadedValues = response;
this.setState({
loadedValues: this.state.loadedValues,
loaded: true
});
}, (error: any) => {
alert(`There was a problem submitting your request: ${error}`);
console.log(error);
});
}
<MyDatePicker
label="Label"
name="Name"
isrequired={false}
onSelectDate={this._handleChanged}
value={this.state.loadedValues["Name"]}
/>
// From MyDatePicker
public render() {
return (
<div>
<DatePicker
label={this.props.label}
strings={DayPickerStrings}
placeholder='Select a date...'
value={this.state.date}
isRequired={this.props.isRequired}
onSelectDate={this._handleChange}
/>
</div>
);
}
There are a few issues here that might be causing what you are seeing.
You should never set state directly outside of the constructor. Doing this.state.value = something in your componentDidMount method is incorrect, as you should never set the state of a React component in any way other than using setState.
You should not read from state in the same method as you set it. React's setState does not change the component's state immediately - state changes are batched, merged together and applied at a later point. If you want to read data from the state when it changes, you should do it in the componentDidUpdate method by comparing old state to new state.
If you try to change your state directly, you'll cause some issues with your component's lifecycle, which might be what is causing the infinite loop. Reading from state right after setting it might also not get you the values you expect.
Try checking out the component lifecycle methods and refactoring your code into something more idiomatic. That might make the problem go away.

When to use 'componentDidUpdate' method?

I wrote dozens of Reactjs files, but I never used the componentDidUpdate method.
Is there any typical example of when need to use this method?
I want a real-world example, not a simple demo.
A simple example would be an app that collects input data from the user and then uses Ajax to upload said data to a database. Here's a simplified example (haven't run it - may have syntax errors):
export default class Task extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: "",
age: "",
country: ""
};
}
componentDidUpdate() {
this._commitAutoSave();
}
_changeName = (e) => {
this.setState({name: e.target.value});
}
_changeAge = (e) => {
this.setState({age: e.target.value});
}
_changeCountry = (e) => {
this.setState({country: e.target.value});
}
_commitAutoSave = () => {
Ajax.postJSON('/someAPI/json/autosave', {
name: this.state.name,
age: this.state.age,
country: this.state.country
});
}
render() {
let {name, age, country} = this.state;
return (
<form>
<input type="text" value={name} onChange={this._changeName} />
<input type="text" value={age} onChange={this._changeAge} />
<input type="text" value={country} onChange={this._changeCountry} />
</form>
);
}
}
So whenever the component has a state change it will autosave the data. There are other ways to implement it too. The componentDidUpdate is particularly useful when an operation needs to happen after the DOM is updated and the update queue is emptied. It's probably most useful on complex renders and state or DOM changes or when you need something to be the absolutely last thing to be executed.
The example above is rather simple though, but probably proves the point. An improvement could be to limit the amount of times the autosave can execute (e.g max every 10 seconds) because right now it will run on every key-stroke.
I made a demo on this fiddle as well to demonstrate.
For more info, refer to the official docs:
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).
Sometimes you might add a state value from props in constructor or componentDidMount, you might need to call setState when the props changed but the component has already mounted so componentDidMount will not execute and neither will constructor; in this particular case, you can use componentDidUpdate since the props have changed, you can call setState in componentDidUpdate with new props.
This lifecycle method is invoked as soon as the updating happens. The most common use case for the componentDidUpdate() method is updating the DOM in response to prop or state changes.
You can call setState() in this lifecycle, but keep in mind that you will need to wrap it in a condition to check for state or prop changes from previous state. Incorrect usage of setState() can lead to an infinite loop.
Take a look at the example below that shows a typical usage example of this lifecycle method.
componentDidUpdate(prevProps) {
//Typical usage, don't forget to compare the props
if (this.props.userName !== prevProps.userName) {
this.fetchData(this.props.userName);
}
}
Notice in the above example that we are comparing the current props to the previous props. This is to check if there has been a change in props from what it currently is. In this case, there won’t be a need to make the API call if the props did not change.
For more info, refer to the official docs:
componentDidUpdate(prevProps){
if (this.state.authToken==null&&prevProps.authToken==null) {
AccountKit.getCurrentAccessToken()
.then(token => {
if (token) {
AccountKit.getCurrentAccount().then(account => {
this.setState({
authToken: token,
loggedAccount: account
});
});
} else {
console.log("No user account logged");
}
})
.catch(e => console.log("Failed to get current access token", e));
}
}
I have used componentDidUpdate() in highchart.
Here is a simple example of this component.
import React, { PropTypes, Component } from 'react';
window.Highcharts = require('highcharts');
export default class Chartline extends React.Component {
constructor(props) {
super(props);
this.state = {
chart: ''
};
}
public componentDidUpdate() {
// console.log(this.props.candidate, 'this.props.candidate')
if (this.props.category) {
const category = this.props.category ? this.props.category : {};
console.log('category', category);
window.Highcharts.chart('jobcontainer_' + category._id, {
title: {
text: ''
},
plotOptions: {
series: {
cursor: 'pointer'
}
},
chart: {
defaultSeriesType: 'spline'
},
xAxis: {
// categories: candidate.dateArr,
categories: ['Day1', 'Day2', 'Day3', 'Day4', 'Day5', 'Day6', 'Day7'],
showEmpty: true
},
labels: {
style: {
color: 'white',
fontSize: '25px',
fontFamily: 'SF UI Text'
}
},
series: [
{
name: 'Low',
color: '#9B260A',
data: category.lowcount
},
{
name: 'High',
color: '#0E5AAB',
data: category.highcount
},
{
name: 'Average',
color: '#12B499',
data: category.averagecount
}
]
});
}
}
public render() {
const category = this.props.category ? this.props.category : {};
console.log('render category', category);
return <div id={'jobcontainer_' + category._id} style={{ maxWidth: '400px', height: '180px' }} />;
}
}
When something in the state has changed and you need to call a side effect (like a request to api - get, put, post, delete). So you need to call componentDidUpdate() because componentDidMount() is already called.
After calling side effect in componentDidUpdate(), you can set the state to new value based on the response data in the then((response) => this.setState({newValue: "here"})).
Please make sure that you need to check prevProps or prevState to avoid infinite loop because when setting state to a new value, the componentDidUpdate() will call again.
There are 2 places to call a side effect for best practice - componentDidMount() and componentDidUpdate()
#K.tin, if you call setState in componentDidUpdate(), would that cause the same data to be fetched again?
For example, [id, data_for_id] are states, and id can be changed by a click counter and data_for_id is fetched from a web API.
Now we click to change Id by setState(), and componentDidUpdate() is executed, which fetches the data_for_id, we do setState for data_for_id, which will trigger another componentDidUpdate().
The first time componentDidUpdate is called, we have prevState.ID = 0 and state.ID=1, so componentDidUpdate is run. The second time we have prevState.ID = 1 and state.ID = 1, and componentDidUpdate can be avoid entirely, which perhaps could also be implemented with shouldComponentUpdate().
Still, this causes TWO rerenders, one for ID change and one for data_for_id change, Ideally, once we detect ID change, data_for_id should be fetched, and we should have [Id, data_for_id] state changed in a single shot, and the rerender happens only once for this change ID click.
So as a general rule, we should not do any setState in componentDidUpdate(), if the change of two or more state components are related, we should perform the changes together in one place and setState in a single shot.
This is just my reasoning. I am not a react guru, so please comment.
You should be careful when it's used. because it's making multiple API calls. Sometimes unlimited calls
componentDidUpdate should not have setState inside of it. you should use componentDidUpdate specially in manipulating the dom base on this.state values. CRUD or updating state values is not good inside of componentDidUpdate. Also dont fetching new data inside of componentDidUpdate as is can cause multiple http request on server and sometimes can cause laggy specially when the code is not structural or multiple use of setState.

Resources