I am just learning React and I was doing the tutorial : https://reactjs.org/tutorial/tutorial.html
In it, we are suppose to create a function that calculate the winner of the tic-tac-toe and we pass to it the list of the squares' contents. I update this list each time a square is clicked. However, I noticed that when I use this.setState to set the squares list of my Board object and then try to have it with this.state.squares I do not get the active state but the previous one.
Like if I add an X in the first square, it will be in this.state.squares at the next move.
Here is the code, I use :
handleClick(square){
let squares = this.state.squares.slice();
squares[square] = this.state.playing;
console.log(squares);
this.setState({squares: squares});
if(this.state.playing == 'X'){
this.state.playing = 'O';
}
else{
this.state.playing = 'X';
}
this.state.winner = calculateWinner(this.state.squares); #If I do that I get the previous state
this.state.winner = calculateWinner(squares); #If I do that I get the current state
}
Why does it do that ?
According to the docs:
Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.
setState is an asynchronous method which effectively means that all other code in your handleClick function will execute before this.state.squares is mutated.
But there's another issue in your code too -- you're also trying to modify state directly:
if(this.state.playing == 'X'){
this.state.playing = 'O';
}
else{
this.state.playing = 'X';
Modifying this.state directly will not re-render your component.
To resolve these issues, you may use a setState callback:
this.setState({squares: squares}, () => {
if(this.state.playing == 'X'){
this.setState({"playing":"O"});
}
else{
this.setState({"playing":"X"});
}
// I'm assuming that `calculateWinner` does not depend on this.state.playing
this.setState({"winner":calculateWinner(this.state.squares));
});
The setState callback is only invoked once the this.state.squares mutation has occurred -- ensuring that this.state.squares is updated before you invoke calculateWinner.
setState is asynchronous but it can take a callback:
this.setState(
{ squares: squares },
() => console.log("whatever")
);
Heres a quick walkthrough of the different methods of the
LifeCycle
of a component.
You must have good understanding of the lifecylce methods to code in react.
It will tell you "
When does state update in React
"
Methods in Mounting Phase:
It begins when an instance of a component is created and when it gets rendered into the DOM.
1.constructor(props) - it is called when the component is first initialized. This method is only called once.
2.componentWillMount() - it is called when a component is about to mount.
3.render() -it is called when a component is rendered.
4.componentDidMount() - it is called when a component has finished mounting.
Methods in Updating Phase:
It begins when a component's properties or state changes.
1.componentWillReceiveProps(nextProps) - it is called when a component has updated and is receiving new props.
2.shouldComponentUpdate(nextProps, nextState) -it is called after receiving props and is about to update. If this method returns false, componentWillUpdate(), render(), and componentDidUpdate() will not execute.
3.componentWillUpdate(nextProps, nextState) -it is called when a component is about to be updated.
4.render() - called when a component is rerendered.
5.componentDidUpdate(prevProps, prevState) -it is called when a component has finished updating.
Methods in Unmounting Phase:
It begins when a component is being removed from the DOM.
1.componentWillUnmount() - it iscalled immediately before a component unmounts.
Related
I want to console.log() the searchActive state before and after it is updated. Initially its value is false. When I put the second console.log after the setSearchActive in a normal order, it runs before the state is updated so both console.logs return false. Thus, I put the second one in a callback function of state updater, however, I get an error which says "Warning: State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect()". and the second log never runs. How can I fix this issue?
function startSearch (e) {
let searchresult = document.querySelector('.search-result');
if(!searchActive&&e.target.value!==null){
searchresult.classList.add('search-active');
console.log(searchActive);
setSearchActive(true,()=>{
console.log(searchActive);
});
}
}
I am new to React, so bear with me please. I have a component that calls another component that takes a property. This property will get it's value on a callback of a function, something like this:
render(){
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp))
this.setState({myProp: p})
});
return <MyComponent myProp={this.state.myProp}/>
}
myFunc will or will not make an API request and depending on that will call the callback sooner or later. This seems to work fine when API request is made and the callback takes longer to return. However, when the request is not needed and callback returns instantaneously (or almost) I am getting a Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
What am I doing wrong and what is the right way to approach this? Where would be the right place to put this code? Basically what I need is to re-render MyComponenent if this.state.myProp changes
You shouldn't be calling setState inside the render method, you might end up having an infinite loop.
The call to myFunc should be somewhere else (depending on the business logic you have). When the function finishes, it will update the state and then trigger a re-render so MyComponent will get the latest value.
UPDATE
I don't know which conditions will require calling myFunc again, but you can do:
state = {
myProp: null // or some other value that MyComponent can handle as a null state
}
componentDidMount () {
myFunc((p) => {
if(!_.isEqual(p, this.state.myProp)) // This is needed only if you get null back from the callback and you don't want to perform an unnecesary state update
this.setState({myProp: p})
}
}
render(){
const { myProp } = this.state
// You can also do if (!myProp) return null
return <MyComponent myProp={myProp}/>
}
Below is some code snippets for the componentWillReceiveProps.
Here bulkUploadRptSuccess, bulkUploadRptError are the array and bulkUploadRptException is the string.
so once state updating two array and one string it is opening the popup.
its working as expected.
But now when clicking on any thing into the application open the pop up everytime.
How can I compare the conditional array check inside the componentWillReceiveProps.
How can I compare the two array value with the equal or not inside this function
Thanks,
componentWillReceiveProps = (nextProps) => {
let { OCFCheckConfig } = this.props;
let { bulkUploadRptSuccess, bulkUploadRptError, bulkUploadRptException } = OCFCheckConfig;
if (nextProps.OCFCheckConfig.bulkUploadRptSuccess.length > 0 || nextProps.OCFCheckConfig.bulkUploadRptError.length > 0) {
this.addPopupOpen();
}
}
ComponentWillReceiveProps doesn't update props. It actually receives updated props when parent re-render or you're connected to redux and your store gets updated. Anyways It is unsafe to use ComponentWillReceiveProps. Now here we have a replacement for it, getDerivedStateFromProps.
If I have the following react component:
class Cmpt extends Component {
setValue( e ) {
this.setState({ value : e.target.value });
}
render() {
return <input value={this.state.val} onChange={this.setValue.bind(this)}/>
}
}
Now this works as expected, editing the text doesn't reset the cursor to the end of the input. If I modify it such that the setState happens in async, the cursor reset occurs:
class Cmpt extends Component {
setValue( e ) {
setTimeout( () =>
this.setState({ value : e.target.value }) );
}
render() {
return <input value={this.state.val} onChange={this.setValue.bind(this)}/>
}
}
Obviously the fix is to not call setState synchronously - but I don't understand why the async version doesn't work properly. In my head, the chain of events is as follows:
User adds a character to an input field, changing it from ACD to ABCD
The value of the input DOM node is changed to ABCD to reflect this
Some time passes
setState is called - changing the state of the react component from ACD to ABCD
a render call is triggered - the React diffing algorithm compares the DOM nodes value (ABCD) to the value of this.state.value (ABCD). It sees that both are the same and therefore doesn't trigger a redraw and the cursor is preserved
Clearly my mental model is wrong because a redraw is being triggered - causing the cursor reset.
Please can anyone explain why?
Thanks!
A state changes will always trigger a new render call. After that React itself decides on what to re-render. But it will always get triggered by changing the state. Even if you do
this.setState({})
it will call the render method.
Sorry guys, found a duplicate question that answers my question:
In ReactJS, why does `setState` behave differently when called synchronously?
I can't figure out how to mark my own question as a duplicate unfortunately :(
My mental model of the order of events is wrong. Apparently react triggers a synchronous re-render at the end of every event handler, so render is getting called after the DOM changes, but before the react state has changed - causing a redraw and a cursor reset
Thanks all
T
I'm trying to use reactjs to update a state, and once it is updated fire an ajax call requesting a new page. Just before the ajax call fires an offset variable is set: var offset = this.state.npp * this.state.page; However I find after clickNextPage() is fired, the value of this.state.page is not updated.
I fundamentally do not understand what is happening here, this appears to be a race condition, because I watch the state change on my render() function with {this.state.page}.
How can I ensure my this.state.page is updated, and then fire findByName()?
clickNextPage: function(event){
console.log("clicked happend")
page = this.state.page;
console.log(page)
page += 1
console.log(page)
this.setState({page: page});
console.log(this.state.page)
this.findByName()
},
JS Console:
clicked happend
0
1
0
setState is asynchronous in that this.state will not be updated right away. The short answer to your quandary is use the callback (the second parameter to setState) which is invoked after the internal state is been updated. For example
this.setState({page: page}, function stateUpdateComplete() {
console.log(this.state.page)
this.findByName();
}.bind(this));
The longer answer is that after you call setState, three functions are called (see here for more details about each https://facebook.github.io/react/docs/component-specs.html):
shouldComponentUpdate this allows you to inspect the previous and new state to determine whether the component should update itself. If you return false, the following functions are not executed (although the this.state will still be updated within your component)
componentWillUpdate this gives you a chance to run any code before the new state is set internally and rendering happens
render this happens between the component "will" and "did" functions.
componentDidUpdate this gives you a chance to run any code after the new state is set and the component has re-rendered itself
When calling this.setState the new state isn't set directly, React puts it in a pending state which by calling this.state can return the old state.
This is because React might batch your state updates and therefore offers no guarantee that this.setState is synchronous.
What you want to do is called this.findByName() within componentDidUpdate, componentWillUpdate or through the callback offered as a second argument to this.setState depending on your use case. Note that the callback to this.setState won't be fired until after your call has passed and the component has re-rendered itself.
Furthermore in your case you could pass a function do this.setState instead of doing a variable dance to increase readability.
this.setState(function (prevState, currentProps) {
return {page: prevState.page + 1}
}, this.findByName);
Using nameless functions this code can be written in a shorter format.
myReactClassFunction = (param) => {
this.setState({
key: value,
},
() => this.myClassFunction());
}