As title, i find that the following code will makes duplicate render() calls:
pressBtn = () => {
console.warn("call completeUserLogin");
// setState is going to update component state.
this.setState({counter1: this.state.counter1+1});
// counterTwoUpdate is going to update redux state.
this.props.counterTwoUpdate(this.props.counter2+1);
}
The situation is just like i am calling setState two times. But two setState can be easily merged with 1 statement such as :
// Before changes (render() call two times)
this.setState({counter1: this.state.counter1+1});
this.setState({counter2: this.state.counter2+1});
// After changes (render() call only once)
this.setState({
counter1: this.state.counter1+1,
counter2: this.state.counter2+1
});
Any tricks to merge setState and redux action so that it will just makes 1 render() call ? Thanks a lot
If you want to update both counters from two different places, you will cause two times to invoke render() method because of two different updates. First, I will recommend you to not save in state what you can achieve from props, such as redux global state. Second, I will advice you to use setState() for both counters, or to invoke action that will update the relevant. Then, you will invoke render() only once.
Related
I have added the below piece of code in my react project to set the state with the response of a method. But the state never updated. It is always null. May i know how to set the state here
_onload():any{
this._DataAccessObj = new DataAccess();
let result = this._DataAccessObj.getRequest(this.props.itemId).then((item:IRequest) =>{
console.log("item");
console.log(item);
this.setState ({
Request: item
});
console.log(this.state.Request);
console.log("setstate");
});
return;
}
From the documentation of ReactJS and lifecycle:
Every second the browser calls the tick() method. Inside it, the Clock
component schedules a UI update by calling setState() with an object
containing the current time. Thanks to the setState() call, React
knows the state has changed, and calls the render() method again to
learn what should be on the screen. This time, this.state.date in the
render() method will be different, and so the render output will
include the updated time. React updates the DOM accordingly.
The last part is important: The actual state object will be update later on the line, not directly when the setState method has been called.
On the same page:
React may batch multiple setState() calls into a single update for
performance.
Because this.props and this.state may be updated asynchronously, you
should not rely on their values for calculating the next state.
I've been trying to make a 2048 like game in react native. I wanted to check if the state of the game is equal to the previous state, but the previous state is always overwritten. I'm not an expert in react, so it's probably an obvious bug that I couldn't notice but after several hours of debugging I couldn't get it working.
let newState = this.state.gameState;
let previous = this.state.gameState;
console.log(previous) // shows the previous state
// calculate newState
console.log(previous) // the variable previous has changed and is in sync with newState
this.setState({ gameState: newState });
There are probably other ways to implement this but this is the easiest and I would have to rewrite a lot of code to make it work without this state checking.
If there are multiple places that update the state, the above approach could result in out-of-sync values. The best solution is using setState() with an updater function:
this.setState(prevState => {
const { gameState: prevGameState } = prevState;
const newGameState = {
...prevGameState,
positionX: prevGameState.positionX + 1,
// ...
};
return { ...prevState, gameState: newGameState };
});
Of course, if your previous gameState includes arrays, objects, etc, your updater will need more code, but the idea is the same: use updater function if you need to use the current state values to generate new state values.
Try to avoid updating state directly because re-rendering won't happen if you mutate state directly.
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if
it were immutable.
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. setState() will always trigger a re-render unless conditional rendering logic is implemented in shouldComponentUpdate().
If mutable objects are being used and the logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
Example-
this.setState(prev=>({
gameState: prev.gameState + 1
}))
for reference check official docs Using State Correctly
componentWillMount() {
let dID=this.props.match.params.datumID;
this.setState({datumID: dID});
console.log(dID);
console.log(this.state.datumID);
}
I'm just trying use setState() method.
But the problem is it is not working.
Here is what I get as output:
First console.log() : 66
Second console.log(): null
this.setState accepts a second argument as callback which is fired after the state is successfully changed.
componentWillMount() {
let dID=this.props.match.params.datumID;
console.log(dID);
this.setState({datumID: dID},()=>console.log(this.state.datumID));
}
side note : Move your code to componentDidMount.
componentWillMount() is invoked immediately before mounting occurs. It
is called before render(), therefore setting state in this method
will not trigger a re-render. Avoid introducing any side-effects or
subscriptions in this method.
Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
If you need to set the state based on the previous state,
read about the updater argument below.
this.setState((prevState, props) => {
return {datumID: props.match.params.datumID};
});
Because SetState() is an asynchronous function, use below code
componentWillMount() {
let dID=this.props.match.params.datumID;
this.setState({datumID: dID},
function(){
console.log(this.state.datumID)});
console.log(dID);
}
and if possible try to avoid compenentWillMount since it would be deprecated soon
setState() being an asynchronous function it is in progress of updating the state and javascript being single-threaded language it will execute the next line of code. So if you want to see the state value set you have to do something like this.
componentDidMount=()=> {
let dID=this.props.match.params.datumID;
this.setState({
datumID: dID
},()=>{
console.log(dID);
console.log(this.state.datumID);
});
}
Also I would recommend you to use componentDidMount as willMount won't work in the future as it is deprecated
this.setState is an asynchronous function that takes time and passes the newly assigned
state value over all of the React's life-cycle functions in order to update a state and trigger the re rendering of a component. Your first console logs out the value that is assigned to set state and the next console logs out the value that is currently residing in the state this.state.datumID. if you need to perform operations after setting state you can pass a callback that is triggered after a state is successfully updated.
this.setState({datumID: dID}, () => console.log(this.state.datumID) ); // this will log the updated value
Another thing I want to point out is componentWillMount will wont work in the future release. If you want to do something after DOM is rendered, use componentDidMount or you can perform pre-render task in the constructor of your Class Component.
My scenario is:
this.setState({firstState:1})
if(condition){
this.setState({secondState:2})
}
firsState should always be set to 1 but the secondState should only be set to 2 if a certain condition is fulfilled.
Is this bad practice because state will be updated twice directly after each other? And will it affect performance?
Is this code better?
if(condition){
this.setState({firtState:1,secondState:2})
}else{
this.setState({firstState:1})
}
You can update it once:
const myAmazingState = {
firstState: 1,
secondState: condition ? 2 : this.state.secondState
}
And then update your state
this.setState({...myAmazingState})
Hope it makes sense
Even if you put multiple setState calls in sequence React may batch them into one transaction call.
So there is no disadvantage.
See React may batch multiple setState() calls into a single update for performance.
React actually provides a way for you to achieve just that. The trouble with your current approach, as was pointed out earlier, is that React may batch the setState, but you can use functional setState. Your code would look like this.
this.setState((state, props) => {
// update state
if(condition) {
//update state 2
}
// return a single new object for your new state
});
React.js is optimized to have setState called quite often, it will actually not always re-render everytime you call setState but in fact will bundle multiple state changes together into one re-render.
In order to change the state twice, you need to do the following to avoid any possible issues as this.setState is an asynchronous function.
this.setState({data: {}}, () => this.setState({data: {name: "tom"}}))
Here you can see, we are passing setState a callback to execute once it has finished setting the state.
In React when you want to update state based on a previous value of the state, due to some asynchronous qualities about the setState method, you need to pass in a function as the first param so that at the time state is actually being updated setState has access to the most up to date version of the state. e.g.
const setStateCallback = prevState => {
return {myInteger: prevState.myInteger + this.props.step};
}
this.setState(setStateCallback);
My question is what is the point of the props argument in this function if state is the only piece of data is potentially out of date. Why not just use
const setStateCallback = prevState => {
return {myInteger: prevState.myInteger + this.props.step};
}
this.setState(setStateCallback);
The way I assume setState is working is that setStateCallback gets stored and will be invoked once React is ready to process all of the setState calls. At that time, yes I would need to have access to the state updates that have happened during with other setState calls in the process. However, when setStateCallback is called any updates that were made to this.props.step in the meantime would still be available on this.props.step because I am referencing the this.props object. If I did this
const step = this.props.step
const setStateCallback = prevState => {
return {myInteger: prevState.myInteger + step};
}
this.setState(setStateCallback);
This is a problem because the value of step (assuming this is a Number or String) is no longer being referenced off the props object and could be out of date.
So given my assumptions are correct, accessing the props argument given to the callback setStateCallback seems unnecessary.
UPDATE
As pointed out by Brad Bumbalough, and a little extra evidence by myself below, the props arg is indeed necessary.
The implications of this finding mean you must be careful whenever accessing this.state or this.props within your custom class methods. For example, for any custom class methods called within a setState callback you need to make sure that any references within those methods or references within subsequent class methods made from that call take state and props as arguments and don't refer to the this.props or this.state.
I think their point is that everything is asynchronous. So, if you are expressly depending on props for your state change, you can't be 100% certain that this.props hasn't changed after the setState call is first queued.
A possible lifecycle of when this would be a problem could be...
Initial render.
Async call to get data from server.
User interaction triggers a setState call that depends on current props (prevState.myInteger + props.step). This will be evaluated immediately with current state/props, and the result gets stored as the new value for the state attribute.
setState call is queued.
Async call returns and updates parent component/store, trigging props to change.
setState call is processed and applied, but the props used to generate the stored state data is now out of sync with the current props the user can see, leading to confusion, chaos, and WW3. Ok, I might have exaggerated there.
The key is that it might change. 99% of the time you probably won't need to drill this specific into the call. But the call is there, if you so need it.
In case anyone else was looking, here's the link to the docs on this.
React may batch multiple setState() calls into a single update for performance.
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument.