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.
Related
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().
How do I set the value of a Reactstrap Input to be empty? I am using code like this.
handleChange = (event) => {
this.setState({message: event.target.value});
};
< Input onChange = {this.handleChange}/>
Also, what is the best practice for getting the value of an input: refs or onChange?
You gotta set a value property for your input which holds the value of your input, so your input looks like this:
<Input value={this.state.message} onChange={this.handleChange} />
and then when you want to clear its value, you just do this:
this.setState({message: ''})
and for your other question, the answer is that the Refs provide a way to access DOM nodes or React elements created in the render method, according to React documentation You can use Refs when:
Managing focus, text selection, or media playback.
Triggering imperative animations.
Integrating with third-party DOM libraries.
and you have to AVOID using refs for anything that can be done declaratively.
Then, here we just use onChange because we don't simply need Refs!
You need to assign state value to your input field.
public class myApp() extends React.Component {
constructor(props){
super(props);
this.state = {
message: ''
};
}
handleChange = (event) => {
this.setState({message: event.target.value});
};
render() {
return (
<Input value={this.state.message} onChange={this.handleChange} />
);
}
}
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)
});
}
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
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