How to update react variable state? - reactjs

I am trying to update the state of the variable "selectedPlayer" however when I setState function the state of this variable does not update, I have tested this through console logs as you can see below.
Below is how I am trying to set the state of the variable and the some of this class file for your knowledge
this.setState({ selectPlayer: player });
this is some of the Component file and a function which i am using to update the state.
class StatApp extends Component {
constructor(props) {
super(props);
this.state = {
selectedPlayer: 'dassdasda',
};
this.selectPlayer = this.selectPlayer.bind(this);
}
selectPlayer = e => {
e.preventDefault();
// console.log(e.target.value); //will give you the value continue
// Store Value
const selectPlayer = this.state.selectedPlayer;
console.log(this.state.selectedPlayer);
console.log(selectPlayer);
// Test to see if we are getting the value from the playerButton
const player = e.target.value;
console.log(player);
this.setState({ selectPlayer: player });
console.log(selectPlayer);
};

setState is asynchronous, so when you are console logging it, state has not yet been updated. You can use the callback form of setState to get the updated state:
this.setState(
{ selectPlayer: player },
() => console.log(this.state.selectPlayer)
);

I guess it'a typo.
try changing
this.setState({ selectPlayer: player });
to
this.setState({ selectedPlayer: player });
Find the other answer for the updated state not being printed on console.

Related

Setting the state in a component from localStorage

I'm building a multi-step form in React and one of my objectives is to save the user's input if they haven't finished filling in the form. I have saved the user's input in the browser's localStorage by using setItem().
The input fields set the local state which in turn get saved to the localStorage.
However, when the page is refreshed, I want to retrieve the data from localStorage and set the state variables from there so that it pre-fills the input fields with the saved data (if that makes sense)
I'm using setState() in componentDidMount() to do this, although I think that's creating an anti-pattern and I'm not entirely sure what that is. It works fine when I use UNSAFE_componentWillMount but I don't want to use a deprecated lifecycle method.
This is my code :
componentDidMount() {
this.formData = JSON.parse(localStorage.getItem('form'));
this.setState({
type: this.formData.type,
subtype: this.formData.subtype,
brand: this.formData.brand
})
}
the idea to use componentDidMount is correct. There is another anti-pattern.
Don't use this.formData = ... outside of component's constructor - https://reactjs.org/docs/react-component.html
Whole working example would look like this. I added callback after setState to show that loading & saving to localStorage actually works.
export default class Hello extends React.Component {
state = {
type: undefined,
subtype: undefined,
brand: 0,
}
componentDidMount() {
const formData = JSON.parse(localStorage.getItem('form')) ?? {};
if (formData) {
formData.brand += 5
this.setState({
type: formData.type,
subtype: formData.subtype,
brand: formData.brand,
}, () => {
console.log('newState', this.state)
localStorage.setItem('form', JSON.stringify(this.state))
})
}
}
render() {
return <h1>Hello {this.state.brand} </h1>
}
}
you can use constructor function if you do not want to retrieve local storage data in componentDidMount()
constructor(){
const formData = JSON.parse(localStorage.getItem('form'));
const { type, subtype, brand } = formdata;
this.setState({ type, subtype, brand });
}
Though I'd suggest to go with didMount.
componentDidMount() {
const formData = JSON.parse(localStorage.getItem('form'));
const { type, subtype, brand } = formdata;
this.setState({ type, subtype, brand });
}

Updating state - why creating a new copy of state when calling setState?

React docs:
Never mutate this.state directly, as calling setState() afterwards
may replace the mutation you made. Treat this.state as if it were
immutable.
That's clear.
class App extends React.Component {
state = {
data: []
}
the following I understand
updateState(event) {
const {name, value} = event.target;
let user = this.state.user; // this is a reference, not a copy...
user[name] = value; //
return this.setState({user}); // so this could replace the previous mutation
}
this following I don't understand
updateState(event) {
const {name, value} = event.target;
let user = {...this.state.user, [name]: value};
this.setState({user});
}
I understand (as in previous example), that I should not either only:
mutate state directly without calling setState; or
mutate it and then use setState afterwards.
However, why can't I just (without direct mutation) call setState without creating a new copy of state (no spread operator/Object.assign)? What would be wrong with the following:
getData = () => {
axios.get("example.com") ...
this.setState({
data:response.data
})
}
Why should it be:
getData = () => {
axios.get("example.com") ...
this.setState({
data:[...data, response.data]
})
}
render (){
...
}
}
What would be wrong with the following:
this.setState({
data: response.data,
});
Absolutely nothing, unless you don't want to replace the contents of this.state.data with response.data.
Why should it be:
this.setState({
data: [...data, response.data],
});
Because with spread you are not loosing the contents of this.state.data - you are basically pushing new response into the data array.
Note: You should use callback inside setState to get access to current data from this.state.
this.setState((prevState) => ({
data: [...prevState.data, response.data],
}));

Is there anyway to sync setState in react

As we've known, setState is async. I've read few questions about setState, on how to use the value right after setState, but those aren't what I need right now.
I'm trying to set value for array List, and then use that List to do a function to get the value for Result. If setState isn't async, then it would be like this
`
handleChange(e) {
const resultList = this.state.list.slice();
resultList[e.target.id] = e.target.value;
this.setState({
list: resultList,
result: this.doSomething(resultList) // this.doSomething(this.state.list)
});
}
`
Is there anyway to achieve this? A documentation or keyword to research would be awesome.
Many thanks
There is a callback parameter to setState which is called after the state has been updated
this.setState({
list: resultList,
result: this.doSomething(resultList)
}, () => {
//do something with the updated this.state
});
You can use async await like
async handleChange(e) {
const resultList = this.state.list.slice();
resultList[e.target.id] = e.target.value;
this.setState({
list: resultList,
result: await this.doSomething(resultList) // this.doSomething(this.state.list)
});
}
The use of this.state together with this.setState is discouraged, exactly because state updates are asynchronous and may result in race conditions.
In case updated state derives from previous state, state updater function should be used. Because it's asynchronous, event object should be treated beforehand, due to how synthetic events work in React:
const { id, value } = e.target;
this.setState(state => {
const list = [...state.list];
list[id] = value;
return {
list,
result: this.doSomething(list)
};
});

State not changing after calling this.setState

I am getting the data from my form component and trying to set the state of my app component with this data.
However, the state.data is an empty object and is not updating the data. I console log the model data before setting it to check if it exists. Their is data within the model.
import React, { Component, Fragment } from "react";
import Form from "../components/Form";
import product from "./product.json";
class App extends Component {
constructor() {
super();
this.state = {
data: {}
};
}
onSubmit = (model) => {
console.log("Outer", model);
this.setState({
data: model
});
console.log("Form: ", this.state);
}
render() {
const fields = product.fields;
return (
<Fragment>
<div>Header</div>
<Form
model={fields}
onSubmit={(model) => {this.onSubmit(model);}}
/>
<div>Footer</div>
</Fragment>
);
}
}
export default App;
setState() is an async call in React. So you won't likely get the updated state value in the next line. To check the updated value on successful state update, you could check in the callback handler.
Change this
onSubmit = (model) => {
console.log("Outer", model);
this.setState({
data: model
});
console.log("Form: ", this.state);
}
to
onSubmit = (model) => {
console.log("Outer", model);
this.setState({
data: model
}, () => {
console.log("Form: ", this.state);
});
}
As per the react docs, setState is an asynchronous call. You can ensure your state has updated to perform a particular action in two ways as shown below:
You can pass the setState a function which will have your current state and props and you the value you return will be your next state of the component.
Keep in mind following:
state is a reference to the component state at the time the change is
being applied. It should not be directly mutated. Instead, changes
should be represented by building a new object based on the input from
state and props.
Following is an example:
this.setState((state, props) => {
//do something
return {counter: state.counter + props.step};
});
You can pass a callback to the setState function as mentioned in Dinesh's
answer. The callback will be executed once the state has been updated successfully hence ensuring you will have the updated state in the call back.
Following is an example:
this.setState({ ...new state }, () => {
// do something
});
Hope it helps.
I just want to add, that if you will do like this its not going to work:
this.setState({things} , console.log(this.state))
You have to pass a refarence to the call back and not the exscutable code itself. If you won't do so, the function will envoke before the state is updated,even you will see the log.

this.setState does not update state

I'm trying to use this.setState within handleFormSubmit however this.setState isn't updating and I'm not sure why. If I run console.log(updatePosition) before this.setState I can that all the data is there. What am I missing? I use similar code for handleChange and I don't have problems.
constructor(props) {
super(props);
let uniqueId = moment().valueOf();
this.state = {
careerHistoryPositions: [{company: '', uniqueId: uniqueId, errors: {} }],
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleFormSubmit(event) {
event.preventDefault();
const { careerHistoryPositions } = this.state;
const updatePosition = this.state.careerHistoryPositions.map((careerHistoryPosition) => {
const errors = careerHistoryValidation(careerHistoryPosition);
return { ...careerHistoryPosition, errors: errors };
});
console.log(updatePosition)
this.setState({ careerHistoryPositions: updatePosition });
}
Keep in mind that the state isn't updated immediately. If you want to check if it's updated use callback function. Something as follows:
this.setState({ careerHistoryPositions: updatePosition }, () => console.log(this.state.careerHistoryPositions);
From the 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. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
Hope this helps.
You should show how you are calling handleFormSubmit chances are that it's bound to a Dom event. So this is not the class/component, instead if you console.log(this); you'll see that it's the Dom element, the form element.
To make your code work as intended, in your component constructor() method, add this to rebind the handler function to the react component's class method, and you'll have access to this.state and this.setState()
this.handleFormSubmit = this.handleFormSubmit.bind(this);

Resources