How react state works? - javascript

I'm working with controlled form and have handleChange function, that get input value and save it in state like state.mainField.firstInput.
handleChange = (e) => {
// the input value was 2, then I enter 3
console.log(this.state.mainField.firstInput); // expect 2
console.log(this.state); // expect { mainField: { firstInput: 3 } }
/*
...
*/
this.setState({ mainField: newData });
}
/*
...
*/
<form>
<input value={this.state.mainField.firstInput} onChange={this.handleChange} />
</form>
When I'm trying print state.mainField.firstInput to the console on the top of the handleChange function I got different result with state in the same field. Exact firstInput property was current state value and property in object this.state was like after setState functions. Why this same values are different?

There are two things to note here
setState is asynchronous and hence it won't reflect the change immediately
Secondly when you log an object with console.log(), it is evaluated after you expand it and hence by that time the value is updated. Hence you see the difference between
console.log(this.state.mainField.firstInput); // expect 2
console.log(this.state); // expect { mainField: { firstInput: 3 } }

JavaScript is an synchronous and single-threaded language.
So it runs line-by-line
You are console-logging before your state changes so It'll obviously give 2.
Even if you console.log after setting the state then also you may not get the expected result because the set state takes time to execute.
// This may or may not work
handleChange = (e) => {
// the input value was 2, then I enter 3
console.log(this.state.mainField.firstInput); // expect 2
this.setState({ mainField: newData });
console.log(this.state); // expect { mainField: { firstInput: 3 } }
}
But this will surely work
handleChange = (e) => {
// the input value was 2, then I enter 3
console.log(this.state.mainField.firstInput); // expect 2
this.setState({ mainField: newData },()=>{
console.log(this.state); // expect { mainField: { firstInput: 3 } }
});
}

Related

how to prevent stacking function callbacks while exiting a screen react/react-native

I have a local state selectedProducts holding a number representing the number of products the client has selected, while exiting the screen I want to update the number in redux so it can be viewed in the cart on a different screen.
but every time my local selectedProducts updates it stacks another function call on beforeRemove event
I know this is a noob problem but I've spent hours trying to find a solution to this problem and advice would be very helpful :)
useEffect(()=>{
navigation.addListener('beforeRemove', () => {
console.log('check this', selectedProducts);
if (!selectedProducts) {
return;
}
dispatch(addToCartMultipleQty(selectedProducts));
});
},[selectedProducts])
selectedProducts is a number state whose initial value is null and on a button click event its value is either incremented or decremented by 1, if its previous value is null then its value is set to 1
Note: I just want to update selectedProducts state's latest value only once in redux when the screen is about to be exited/unmonted
You can try this:
useEffect(()=>{
navigation.addListener('beforeRemove', () => {
console.log('check this', selectedProducts);
if (!selectedProducts) {
return;
}
dispatch(addToCartMultipleQty(selectedProducts));
});
return () => {
navigation.removeListener('beforeRemove');
}
}, [selectedProducts])
Add that in return at the end of useEffect it will work as componentWillUnmount in functional component
useEffect(() => {
return () => {
// Anything in here is fired on component unmount.
}
}, [])
Edit: In Your case
useEffect(() => {
console.log('check this', selectedProducts);
if (!selectedProducts) {
return;
}
return () => {
// Anything in here is fired on component unmount.
if (selectedProducts) {
dispatch(addToCartMultipleQty(selectedProducts));
}
};
}, [selectedProducts]);

Setting state inside React functional setState()

Is one allowed to call a setState inside the function passed into setState. for instance im trying to check for errors in user input and im calling the error checker function inside the setInpuForm, I know I can call the checkerror() from outside the setInputForm i.e inside the change handler, but out of curiosity, i decided to try it this way. But I discovered after typing the first character into the input field no changes but from the second entry up wards,its displayed. When i ran this it worked, the issue is that the very first time the input changed, the value was not updated but subsequently it worked
const errorChecker = (inputValue, element)=>{
//check for password error
if (element==='password') {
if (inputValue.length<=6) {
console.log('function error checker called')
setInputForm((form)=>{
console.log('after error check')
console.log(form.password.value)
return {
...form,
[element]: {
...form[element],
error: 'Password can not be less than 6'
}
}
})
} else {
setInputForm((form) => {
return {
...form,
[element]: {
...form[element],
error: ''
}
}
})
}
}}
<Input
key={inputForm[it].label}
changed={(event) => {
const val = event.target.value
setInputForm((form)=>{
console.log(form.password.value)
console.log('before error check')
errorChecker(val, it)
console.log('from set valuie')
return {
...form,
[it]: {
...form[it],
value: val
}
}
})
}}
I believe your issue is that you are calling two setState functions one after another, both of which rely on the previousState value. setState as you may know is not synchronous, and needs some time to finish its execution. In the meantime what you would be using in your errorChecker function would be the last state registered.
In order to avoid this, in a class based component you would usually include a callback function on your this.setState function. In a functional component you cannot do that. Instead you can use useEffect in order to detect changes to a certain state, and trigger a function like errorChecker based on those changes.
For example:
useEffect(() => {
if (inputForm[it].value !== "") {
errorChecker(inputForm[it].value, inputForm[it]);
}
}, [inputForm[it].value);

React state not updating using immutability helper update function

Hi I am using Formik in my component, where a method call used like below
addMetadata(values) {
console.log(values);
let newState = update(this.state, {
pro: { $set: values}
});
console.log(newState); // this point result print as expected
this.setState(newState);
console.log(this.state); // but here state not showing update result
}
where my state looks like
this.state = {
pro: {
key1: '',
key2: []
key3: {}
}
}
but states are not updating, can anyone know why ?
this.setState(newState) is asynchronous (or at least, it can be). Putting a log statement on the next line will not work, because the state hasn't been set yet.
In the rare cases where you need to know when the setState is done, you can provide a callback as the second argument to setState, which will be called once its complete:
this.setState(
newState,
() => {
console.log(this.state);
}
)

State is wrongly updated in react app , object however shows correct values [duplicate]

This question already has answers here:
Why does calling react setState method not mutate the state immediately?
(9 answers)
Closed 4 years ago.
I have the following state in my React component:
state = {
genderRadioClick : false,
ageRadioClick : false
}
I have the following function that gets called onChange of every radio button in the component:
_updateRadioButtonValuesObsolte = (e) => {
let newStateOfCheckboxs = {
genderRadioClick : Array.from(document.getElementsByName('customer_gender')).some( (elem , idx) => { return elem.checked }),
ageRadioClick : Array.from(document.getElementsByName('customer_age')).some( (elem , idx) => { return elem.checked })
}
console.log(newStateOfCheckboxs);
this.setState({
...newStateOfCheckboxs
});
console.log(this.state);
}
When i check one of the radio button and the following line of code runs:
console.log(newStateOfCheckboxs); // {genderRadioClick: true, ageRadioClick: false}
Which is correct , but the output of the below line:
console.log(this.state); // {genderRadioClick: false, ageRadioClick: false}
is wrong , Why are the values of the keys in the compomemnt state wrong ? Even though i am updating as below:
this.setState({
...newStateOfCheckboxs
});
What am i doing wrong here ?
setState is an asynchronous function
this.setState({
...newStateOfCheckboxs
});
console.log(this.state);
By this you are expecting state to be immediately updated after setState is called. But you can follow a callback approach
this.setState({
...newStateOfCheckboxs
}, function(){
console.log(this.state);
});

use state instead of outside class variable?

I want to use my data which keep in my state instead of the outside class variable (I mean languages if u look at the code below)
In the getSuggestion I change languages.filter(lang.... to this.state.myState.filter(lang... but it's not work
It seem like this.state.myState can't be reach
The error appear at the line return inputLength === 0 ? [] : this.state.myState.filter(lang => in the getSuggestion
import Autosuggest from 'react-autosuggest';
// Imagine you have a list of languages that you'd like to autosuggest.
const languages = [
{
name: 'C',
year: 1972
},
{
name: 'Elm',
year: 2012
},
];
// Teach Autosuggest how to calculate suggestions for any given input value.
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
return inputLength === 0 ? [] : languages.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
);
};
// When suggestion is clicked, Autosuggest needs to populate the input
// based on the clicked suggestion. Teach Autosuggest how to calculate the
// input value for every given suggestion.
const getSuggestionValue = suggestion => suggestion.name;
// Use your imagination to render suggestions.
const renderSuggestion = suggestion => (
<div>
{suggestion.name}
</div>
);
class Example extends React.Component {
constructor() {
super();
// Autosuggest is a controlled component.
// This means that you need to provide an input value
// and an onChange handler that updates this value (see below).
// Suggestions also need to be provided to the Autosuggest,
// and they are initially empty because the Autosuggest is closed.
this.state = {
value: '',
suggestions: []
myState: [
{
name: 'C',
year: 1972
},
{
name: 'Elm',
year: 2012
}
] ,
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
// Autosuggest will call this function every time you need to update suggestions.
// You already implemented this logic above, so just use it.
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
// Autosuggest will call this function every time you need to clear suggestions.
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render() {
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input.
const inputProps = {
placeholder: 'Type a programming language',
value,
onChange: this.onChange
};
// Finally, render it!
return (
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
);
}
}
It looks like you are attempting to access this in a function outside of your class, so your this will not be defined in getSuggestions.
If getSuggestions needs to be defined outside of your class, you'll need to make two changes:
define getSuggestions as a normal function as opposed to an arrow function so that we can set getSuggestions's this object manually.
Use Function.prototype.call() to call getSuggestions with a specified value for this.
I wrote up a few examples to show my point. First, notice in the example below, where getSuggestions is an arrow function and it is called by method of Example class:
const getThis = () => {
console.log(this === window || this === undefined); // this is coerced to window or undefined (Strict Mode)
};
class Example {
onSuggestionsFetchRequested() {
console.log(this); // prints correctly
getThis();
};
}
var x = new Example();
x.onSuggestionsFetchRequested(); // prints true once
In getThis, the this object will either be Window or undefined (Strict Mode).
To make the setup above work, we make the two changes proposed above:
const getThis = function() { // change #1: use function() {} vs. () => {}
console.log(this); // correct value for this
};
class Example {
onSuggestionsFetchRequested() {
console.log(this);
getThis.call(this); // change #2: use call()
};
// NOTE: in your code, define onSuggestionsFetchRequested using arrow function syntax like so:
// onSuggestionsFetchRequested = () => { ... };
// I did not use the arrow syntax above so that this example would run in the browser,
// but you would need to in order to use onSuggestionsFetchRequested in callbacks.
}
var x = new Example();
x.onSuggestionsFetchRequested();
Note: I made a small modification above to make the snippet run in the browser. Namely, I defined onSuggestionsFetchRequest as a class method as opposed to a class property (using arrow function syntax) as you have in your code. For your use case, you'll want to keep onSuggestionsFetchRequest defined using arrow function syntax so that it can be used in callbacks.

Categories