React function isn't behaving as its expected to - javascript

Heres a function from my react app:
handleSubmit(evt) {
evt.preventDefault();
this.setState({
width: "",
height: "",
color: ""
});
console.log(this.state)
};
In my input, I set the value of input to the width, height and color. this handleSubmit function is happens when a form in filled.
But I have set the state via setState before the console.log line. So this will replace the values from the form, before the console.log is called. I should get
{width :" ", height :" ", color :" "}
But instead, I get the value that was set by the input. But it seems like setState is only working when the full function is done, not before the log. Why?

But it seems like setState is only working when the full function is done, not before the log. Why?
setState doesn't change the state immediately. Ref: https://reactjs.org/docs/react-component.html#setstate
If you want to do something right after the state change, use a callback function.
handleSubmit(evt) {
evt.preventDefault();
this.setState({
width: "",
height: "",
color: ""
}, () => {
console.log(this.state)
});
};

You can use functional setState rather than passing an object. Instead you can pass a function to setState which takes the current state and props as arguments, this function returns an object which will be merged into the old state.
For example,
this.setState((state, props) => {
return {
counter: state.counter + 1
}
});
Calls to setState get queued and then executed in the order they were called. When they are executed the function receives the latest state instead of receiving old state.

Related

React - Children Accessing Parents state too early?

When using a callback from a child to app.js, the callback function in app.js sets the state for an element.
In the same child-function, on the next line, the function accesses this state, which is passed along as a prop.
Is there a way to make sure that this next line executes with the updated state already? If not, whats a better way to implement this?
Child Component
function handleCountrySelected(selected) {
let selectedValues = {
countries: selected,
planningGroups: null,
};
onFilterChange(selectedValues); // changes selectedFilters.country via callback
console.log(selectedFilters.countries); // still old state. selectedFilters is a prop from apps.state. Here it is still showing the old value
filters_changed();
autoApply && applyFilter(); // applyFilter fails because its accessing old state, still.
}
App.js
updateSelectedFilters(selectedValues) {
this.setState({
selectedFilters: {
...this.state.selectedFilters,
...selectedValues
}
}
...
<NavbarTopFilters onApplyFilters={this.onApplyFilters}
selectedFilters={this.state.selectedFilters}
onFilterChange={this.updateSelectedFilters}
/>
setState is an async function. You need to pass in the rest of the lines as a callback.
function updateSelectedFilters(selectedValues, callback) {
this.setState({selectedFilters: {...}}, callback);
...
}
onFilterChange(selectedValues, () => { console.log(...) });

setState doesn't update all state properties after call

The thing is, that I use setState to update state.n and state.data. I want to call setState onClick, using handler. After calling setState, I perform a callback console.log to watch the changed state properties, but only state.data is changed, not state.n.
handler(e) {
this.setState((state) => {
let arr = state.data;
arr.push(fakeTableData[state.n+1]);
this.setState({n: state.n+1, data: arr});
}, console.log.bind(null, this.state.n));
}
After one handler used, I expect to see state.n incremented, but I had only state.data changed, not state.n. And I recieve a warning, like this: "Warning: An update (setState, replaceState, or forceUpdate) was scheduled from inside an update function. Update functions should be pure, with zero side-effects. Consider using componentDidUpdate or a callback." I've read a tutorial on the main react site, but there not enough info.
let's try let arr = [...state.data]
You should not use setState inside function that sets state. That is why you got an error. Instead you should return state. Try this:
handler = (e) => {
this.setState((state) => {
let arr = state.data;
arr.push(fakeTableData[state.n+1]);
return {n: state.n+1, data: arr};
}, () => { console.log(this.state.n) });
}

Ensuring React state has updated for game loop

I am writing a version of Conway's Game of Life in React. The component's state contains the grid describing which of the cells is alive at the current time. In each game loop, the new grid is calculated and the state is updated with the next iteration.
It occurs to me that since setState is asynchronous, when repeatedly calling the iterate function with setInterval, I am not guaranteed to be using the current version of grid each time iterate runs.
Is there an alternative to using setInterval in React that would avoid any potential issues caused by setState being asynchronous?
Here are the relevant functions that describe the game loop:
go = () => {
const { tickInterval } = this.state;
this.timerId = setInterval(this.iterate, 570 - tickInterval);
this.setState({
running: true,
});
};
iterate = () => {
const { grid, gridSize, ticks } = this.state;
const nextGrid = getNextIteration(grid, gridSize);
this.setState({
grid: nextGrid,
ticks: ticks + 1,
});
};
If you need to set state based on a current state, it is wrong to directly rely on this.state, because it may be updated asynchronously. What you need to do is to pass a function to setState instead of an object:
this.setState((state, props) => ({
// updated state
}));
And in your case it would be something like:
iterate = () => {
this.setState(state => {
const { grid, gridSize, ticks } = state;
const nextGrid = getNextIteration(grid, gridSize);
return {
grid: nextGrid,
ticks: ticks + 1
}
});
};
SetState is Asynchronous
this.setState({
running: true,
});
To make it synchronously execute a method:
this.setState({
value: true
}, function() {
this.functionCall()
})
If you have a look at the react official documentation, the setState api does take a callback in following format:
setState(updater[, callback])
Here the first argument will be your modified state object and second argument would be callback function to be executed when setState has completed execution.
As per the official docs:
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)), either of which are guaranteed to fire after the update
has been applied. If you need to set the state based on the previous
state, read about the updater argument below.
You can have a look at official docs to get more information on this.

react setState doesnt work neither its callback

when i call this.setState in my code it seems to do nothing at all
this.setState({array:pre.array},()=>{console.log("setState")})
i can't even see the log from the call back function here is my code
class Watch extends React.Component{
constructor(pre) {
super(pre);
this.state = { color: "green" }
this.setState(() => {
return {array:newarray}
}, console.log("setState works"))
p.s:i have tried to put setState in onClick function, in componentDidMount(),componentDidUpdate() still dosent work with no error
It seems like when using setState callback system, the first parameter (updater) needed to be in the form of a function as well. So you can try something like:
onEditText=(value)=>{
this.setState((state, props)=>{
return {text: value}
}, this.onTextEdited);
}
onTextEdited=()=>{
//this will be printed with the updated value
console.log('onTextEdited', this.state.text)
}
See if this helps.

this.state.foo is different in _onChange and different in render()?

In _onChange method, I wait for a change in this.state.name. On the change _updateArticle method is called:
_onChange() {
var team = this.getTeamState();
this.setState({name: team});
console.log(this.state.name); //"Boston Celtics" //this should have changed but it didn't
this.unbind("articles");
this._updateArticle();
},
then in _updateArticle a new reference is created using this new state.
_updateArticle(team) {
var teamRef = new Firebase(this.props.baseUrl + team + "/results");
console.log(this.state.team); // "Boston Celtics" //this should have been new but its not
this.bindAsArray(teamRef, 'articles');
},
In render method however just to check the state I put console.log to check. In render, this.state.name has been updated properly. My question is why is this.state different outside of render function?.
As per the API docs:
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.
If you really want to access state right after calling setState, use the callback parameter in the setState method:
setState(function|object nextState[, function callback])
So your code should look like:
_onChange() {
var team = this.getTeamState();
this.setState({name: team}, function() {
console.log(this.state.name);
this.unbind("articles");
this._updateArticle(this.state.team);
});
}
Hope this helps.

Categories

Resources