React this.setState is undefined inside handlechange - reactjs

I have a dropdown for search via location
constructor() {
super();
this.state = {
formData:{ }
};
this.handleSelect = this.handleSelect.bind(this);
}
<div class="form-group emp-searc-location ">
<select id="emp_location" onChange={this.handleSelect} name="emp_location" value={this.state.formData.emp_location} class="form-control">
<option value="">Select Location name</option>
{this.state.emplocation.map(({ branch_location, id }, index) => (
<option value={branch_location}>{branch_location}</option>
))}
</select>
</div>
And my function
handleSelect=async(e)=>{
this.setState({
formData: {
...this.state.formData,
[e.target.name]: e.target.value,
},
});
console.log(this.state.formData);
}
And formData seems empty.but when i console console.log(e.target.value) i got correct value.but when i console console.log(this.state.formData); i got empty value.any help would be highly appreciated.

As stated in the docs, setState has an additional callback parameter: React - setState()
You can utilize the callback parameter and check the updated state there.
See also: how-to-access-updated-state-value-in-same-function-that-used-to-set-state-value
Also, may I know if the async keyword is necessary?

In your case, The value will be properly assigned to state. But If you put console.log(this.state.formData); immediately after setState. The value will not be replicated. For this, You can put console.log inside componentDidUpdate() like,
componentDidUpdate() {
console.log(this.state.formData);
}
You can check the value using the componentDidUpdate().

Related

react is not updating components using state in class

so first of all I am trying to assign null values to the state component and then assign them values later so here is my constructor of a class
constructor(props){
super(props);
this.state = {
Countrys:null,
States:null,
Citys:null
}
}
and then I am asigning them value is ComponentDidUpdate function so here is my componentDidMount function
componentDidMount() {
navigator.geolocation.getCurrentPosition(function(position) {
console.log(position)
console.log("Latitude is :", position.coords.latitude);
console.log("Longitude is :", position.coords.longitude);
});
this.setState({
Countrys:Country.getAllCountries(),
States:State.getAllStates(),
Citys:City.getAllCities()
})
/*console.log("Country:: "+Country.getAllCountries()[0].name);
console.log("City:: "+City.getAllCities()[0].name);*/
}
and then I am trying to access them in my jsx elements in return using map like this
{this.state.Citys.map((ele ,index) =>
<option value={`${ele}`} className="d-inline mx-2 col-sm-5" onClick={({target}) => target.classList.toggle('bg-primary')}>
{ele.name}
</option>
)}
but its showing me error that
TypeError: Cannot read properties of null (reading 'map')
can anyone tell me what is wrong here or how to correct it
and when I am trying to assign City.getAllCities() like functions directly to this.state instead of assigning them with null it shows me page unresponsive
And City.getAllCities(), Country.getAllCountries(), State.getAllStates() are from npm package "country-state-city"
If, like you mentioned, you're using country-state-city, then there is no reason to defer loading those three values until componentDidMount at all: they're static imports from the country-state-city package.
Just import them at the top of your file, create your initial state in your constructor, assign those values, and you're done without any null states during render.
import { Component } from "react";
import { Country, State, City } from "country-state-city";
...
export class YourClass extends Component {
constructor(props) {
super(props);
this.state = {
countries: Country.getAllCountries(),
states: State.getAllStates(),
cities: City.getAllCities()
};
}
render() { ... }
}
The first render will have state.Citys set to null, as you've set in the constructor.
ComponentDidMount will only fire after the first render.
you should initialized the state in the constructor
As Bernardo Ferreira Bastos Braga mentioned, state.Citys is null in the first render.
One thing you can do to avoid the error is to render conditionally.
{this.state.Citys !== null && this.state.Citys.map((ele ,index) =>
<option value={`${ele}`} className="d-inline mx-2 col-sm-5" onClick={({target}) => target.classList.toggle('bg-primary')}>
{ele.name}
</option>
)}
Or
{this.state.Citys === null ? <SomeOtherComponent/Element/EmptyFragment/etc.. /> : this.state.Citys.map((ele ,index) =>
<option value={`${ele}`} className="d-inline mx-2 col-sm-5" onClick={({target}) => target.classList.toggle('bg-primary')}>
{ele.name}
</option>
)}
In these examples, the map function won't be called if state.Citys is null.

function onHandleChange won't work until after I've selected both options in the select tag

The function onHandleChange won't work until after I've selected both options in the select tag, then it start to work properly.I'm trying to render a chart based on which data the function gets. I'm curious to understand why this happens and where I'm making my mistake. Could someone please point to me where I'm making my mistake or if there's any mistake at all? I'm fairly new to React.js and programming in general.
This doesn't seemed to work until after I've selected both options from the select tag:
onHandleChange = (data, listDataFromChild) => {
if(this.state.listDataFromChild === 'oneYear'){
this.setState({
data:oneYear
})
} else if(this.state.listDataFromChild === 'twoYear'){
this.setState({
data:twoYear
})
}
}
Here's the component from which the function onHandleChange gets it's data from:
export class SelectionBoxYear extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'oneYear'
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
value: e.target.value
},()=>{
let { value } = this.state;
this.props.callbackFromParent(value);
this.props.onHandleChange(value, this.props.dataFromParent)
});
}
render() {
return(
<div>
<label>
<p>Select the year of the investment:</p>
<select value={this.state.value} onChange={this.handleChange}>
<option value="oneYear">One year ago</option>
<option value="twoYear">Two years ago</option>
</select>
</label>
</div>
)
}
}
Here's the full code: https://github.com/vtrpza/chart/tree/master/my-app/src
From React's documentation
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.
In SelectionBoxYear.js file you need to put the onHandleChange and callbackFromParent functions in the callback of set state
handleChange(e) {
this.setState({
value: e.target.value
},()=>{
let { value } = this.state;
this.props.callbackFromParent(value);
this.props.onHandleChange(value, this.props.dataFromParent)
});
}

React-select component value not updating.

Just wondering what I am doing wrong here. When I select a value out of the list the component input field is not being filled in with the value.
class Search extends Component {
constructor(props) {
super(props);
this.state = {
name: 'order_select_field',
placeholder: "Make a selection",
}
}
componentWillMount () {
fetch('http://apirequest.com')
.then( function(response) {
return response.json();
})
.then(function(json) {
this.setState({options: json});
}.bind(this))
}
handleChange(event) {
this.setState({ })
}
render() {
return (
<div>
<Select
name={this.state.name}
options={this.state.options}
placeholder={this.state.placeholder}
/>
</div>
)
}
}
Your main issue is your handleChange method doesn't set the value
handleChange = (event) => {
this.setState({
...this.state,
value: event.target.value
})
}
With a vanilla <select> component, the onChange event would have a DOMElement reference at event.target, and react provides the value prop on the DOM element do you can use it to update your state. You're 3rd-party <Select> component might have a different event signature or expectation.
Also, since I don't know what library you're using, I've provided the state key which tracks your value as "yourSelectKey", but you can replace this with the correct key. If it's a nested property (part of an object), you may have to add the spread operator so that the other values get copied over as well.
And you need to add the onChange event handler to your select component. I recommend you follow the react docs instead of using that library.
<select
value={this.state.value}
onChange={this.handleChange}>
name={this.state.name}>
<option value="value1">Value 1</option>
<option value="value2">Value 2</option>
<option value="value3">Value 3</option>
</select>
Other issues you're facing:
You need to bind handleChange to your object instance. You can either do this in the constructor with a line this.handleChange = this.handleChange.bind(this) or by declaring handleChange as an instance variable, as I have done above.
as the commenter said, you should avoid doing the fetch call in componentWillMount, but should use componentDidMount. This is a common mistake for beginners.

React: programmatically change component value doesn't trigger onChange event

I have one select field inside a React component, it value it's set through the component state an has an function attached to the onChange event. If I change the select field value manually, the onChange event it's triggered, but if I change it by changing the state value from another function it is not. It's there a way to trigger the event programmatically?
Edit:
Below is a basic example on what I need to achieve. The idea is that when the handleChange1() changes the value of state.val2 (and therefore change the option selected on the second select field) the handleChange2() is also triggered so the synthetic event is passed to the parent function (in the actual code, the select fields are another components):
class Component extends React.Component {
state = {
val1: 1,
val2: 1,
}
handleChange1 = (event) => {
const val2 = event.target.value === 3 ? 1 : null;
this.setState({
val1: event.target.value,
});
if (event.target.value === 3) {
this.setState({
val2: 1,
});
}
this.props.parentFunction(event);
}
handleChange2 = (event) => {
this.setState({
val2: event.target.value,
});
this.props.parentFunction(event);
}
render() {
return (
<div>
<select value={val1} onChange={this.handleChange1}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<select value={val2} onChange={this.handleChange2}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
);
}
};
Yes, there is a way! React has logic that prevents onChange from firing when an input's value is set programmatically, but it can be worked around.
Instead of:
input.value = 'foo';
Do this:
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
setter.call(input, 'foo');
input.dispatchEvent(new Event('input', { bubbles: true }));
Check out this article for the full explanation.
In case it's not clear, the value of input is the DOM element that you would get from a ref. Example:
function SomeComponent({ onChange }) {
const ref = useRef();
useEffect(() => {
setInterval(() => {
const input = ref.current;
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
setter.call(input, new Date().toISOString());
input.dispatchEvent(new Event('input', { bubbles: true }));
}, 10000);
});
return <input type="text" ref="ref" onChange={onChange} />;
}
This component would update its input with a date string & trigger the onChange callback with the new value every 10 seconds.
You should wrap your input in a dedicated component to customize the desired behavior. Something like :
class Input extends React.Component {
constructor(props){
super(props);
this.state = {
value: props.value
}
}
componentWillReceiveProps(nextProps) {
this.setState({
value: nextProps.value,
});
this.props.onChange(nextProps.value);
}
updateValue(ev) {
this.setState({
value: ev.target.value,
});
this.props.onChange(ev.target.value);
}
render() {
return (
<input
onChange={this.updateValue.bind(this)}
value={this.state.value}
{...this.props}
/>
)
}
}
and use it like:
<Input value="test" onChange={someAction} />
note that because your input is in a controlled state, value must never be null nor undefined.
I had the same problem and fortunately #gouroujo's answer works for me, but as I checked the documentation its name has changed to UNSAFE_componentWillReceiveProps() and docs say:
Note
This lifecycle was previously named componentWillReceiveProps. That name will continue to work until version 17. Use the rename-unsafe-lifecycles codemod to automatically update your components.
More details about UNSAFE_componentWillReceiveProps()
Another solution is to set a key on the component so that react can
create a new component instance rather than update the current one.
More details about Fully uncontrolled component with a key

How to get the latest value from selected drop down

i have this dropdonw that populates dynamically. when the user selects an item it should set the
state 'selectedValue' with the 'SelectedValue'. i have written the folllowng code but when i ran this code the alert() always display the old value not the newly selected value. why is that?
the function in react class is this
ddlProdCatsChanegeEvent: function(e) {
  
   if (this.state.isMounted)
   {
       var ele = document.getElementById('ddlCategories');
       var seleValue = ele.options[ele.selectedIndex].text;
       this.setState({selectedValue:seleValue});
       alert(this.state.selectedValue);//this always display the old selected value NOT THE new one
   }
},
the state is this:
getInitialState:function(){
   return{data1:[], data2:[], isMounted:false, selectedValue:''}
}
First of all I will recommend you to make use of refs to access the dom element instead of plain javascript. Not that its necessary but because its JSX syntax and you can use it.
Secondly, setState takes some time to mutate the state and thats the reason you are seeing the previouly selected value because it has not been changed before the alert is being triggered.
Put the alert box in the setState callback method as
this.setState({selectedValue: value}, function(){
alert(this.state.selectedValue);
});
Complete code.
var Hello = React.createClass({
getInitialState: function() {
return{ selectedValue:''}
},
handleChange: function(e) {
var value = ReactDOM.findDOMNode(this.refs.selectValue).value;
this.setState({selectedValue: value}, function(){
alert(this.state.selectedValue);
});
},
render: function() {
return (<div>
Hello {this.props.name}
<div>
<select ref="selectValue" onChange={this.handleChange}>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="mercedes">Mercedes</option>
<option value="audi">Audi</option>
</select>
</div>
</div>
)}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
JSFIDDLE
UPDATE
Take a look at this example
React highly discourages manipulating DOM directly, or using methods like getElementById(). So writing a solution w.r.t. your code would be wrong. Instead, I'm going to drop an example that's meant to help you understand how react works and how to implement what you intend.
You could always use ReactDOM, but it would be an overkill. You can instead use event.target.value to directly get the updated value from the <select /> box. How you populate the <select /> box is entirely up to you. I prefer using Array.map() to iterate over the data and return a set of <option />s.
Also note that () => {} is arrow function from es6. You can replace that with es5 function() {} anytime.
import React from 'react';
class SelectExample extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedValue: 0
}
this.updateValue = this.updateValue.bind(this);
}
updateValue(value) {
this.setState({
selectedValue: value
}, () => alert(value));
}
render() {
const dataSet = [1, 2, 3, 4];
return (
<div>
<select value={this.state.selectedValue} onChange={(e) => this.updateValue(+e.target.value)}>
<option value={0}>Default Value</option>
{
dataSet.map((item, idx) => <option value={item} key={idx}>{"Example " + item}</option>)
}
</select>
</div>
)
}
}
This code is not tested. So you may want to clear some errors, if any. You're going totally against React's use case by using document.getElementById. Please change your code to something like I have posted here.
Also read Controlled Component

Resources