Why is the <select> I create in the constructor not updated when I pick another flavor in it? The other select, as well as the text, is updated.
class ConstructorComponent extends React.Component {
constructor() {
super();
this.state = {
icecream: 'vanilla',
};
this.select = (
<select
value={this.state.icecream}
onChange={this.onChange}
>
<option value="chocolate">Chocolate</option>
<option value="vanilla">Vanilla</option>
<option value="strawberry">Strawberry</option>
</select>
);
}
onChange = event => {
this.setState({
icecream: event.target.value,
});
};
render() {
return (
<div>
Icecream flavor: {this.state.icecream}
<br />
{this.select}
<br />
<select
value={this.state.icecream}
onChange={this.onChange}
>
<option value="chocolate">Chocolate</option>
<option value="vanilla">Vanilla</option>
<option value="strawberry">Strawberry</option>
</select>
</div>
);
}
}
ReactDOM.render(
<ConstructorComponent />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
I made it work by cloning this.select in render() like so: {React.cloneElement(this.select, {value: this.state.icecream})}
But making this.select a method instead, as suggested by several answers to my question is probably better. I'll see what works best with my actual code, and not just this dumbed down example :)
Because this.state.icecream in your first select is interpreted only once, when you define this.select in the constructor. So when your ConstructorComponent rerenders a second time when its state changes, there is nothing to update in your first select.
You need to define this.select as a function returning the <select>, like this:
this.select = () => {
return (
<select
value={this.state.icecream}
onChange={this.onChange}
>
<option value="chocolate">Chocolate</option>
<option value="vanilla">Vanilla</option>
<option value="strawberry">Strawberry</option>
</select>
)
}
And call this.select() in the render.
You can also create a reference to the <select> in the render by using the prop ref:
render() {
<select
value={this.state.icecream}
onChange={this.onChange}
ref={select => (this.select = select)} // the magic happens here
>
<option value="chocolate">Chocolate</option>
<option value="vanilla">Vanilla</option>
<option value="strawberry">Strawberry</option>
</select>
}
In doing so, you don't need to define this.select as a function in your constructor.
The first select doesn't work because it's only ever rendered once, in the constructor. The second select works because it's re-rendered every time the app state updates.
If it helps, don't think of JSX elements as "instances" of actual HTML elements. Think of them as a simple means of how you want your app to look in relation to your state. If my state is "a", then I render a select with "a", and if my state is "b", then I render a select with "b", and so on.
The first select box on the page is only rendered in the constructor which is called just once, thus the component will always remain the same as it does when it is first rendered. In other words it is static. The second select box is going to be re-rendered each time the component is updated.
In order to fix your code you can change your line in the constructor to a function:
this.select = () => (
<select
value={this.state.icecream}
onChange={this.onChange}
>
<option value="chocolate">Chocolate</option>
<option value="vanilla">Vanilla</option>
<option value="strawberry">Strawberry</option>
</select>
);
Then change your return statement to call this function:
<br />
{this.select()}
<br />
Related
Here I however tried to make the second div visible if a value is selected in the first dropdown. But I want to make a form where there will be 3 dropdown but only the first will be visible where the user will select any value and the 2nd dropdown will be accordingly. I even need to fetch the values using API. Here's an example of the form what I'm trying to make : https://donate.alauddintrust.com Thanking you!
import React from 'react';
import Section2 from '../Sections/Section2'
class App1 extends React.Component {
//set the statte default value
constructor(props) {
super(props);
this.state = {value: 'hide'};
}
// set the state value based on select option value
divstatus = (e) =>{
this.setState({value: e.target.value});
}
render() {
return (
<>
<div className="container">
<header class="page-header">
<h1 className={['text-warning']}>Donate <i>Now</i></h1>
</header>
<form className="donation-form form-horizontal container" id="donation-form">
<div id="donation-type-group" className="form-group">
<label htmlFor="recurrence">Donation Type <span className="required">*</span></label>
<select id="donation_type" onChange={this.divstatus} name="donation_type" className="form-control">
<option value="show">Select Donation Type</option>
<option value="hide">Sadaqa</option>
<option value="2">Fitrana</option>
<option value="3">Zakat</option>
<option value="4">Fidya</option>
<option value="5">Qurbani</option>
<option value="6">Donation</option>
<option value="15">Kaffara</option>
</select>
</div>
<div className={this.state.value}>
<div id="donation-type-group" className={['form-group']}>
<label htmlFor="recurrence">Programs<span className="required">*</span></label>
<select id="donation_type" name="donation_type" className="form-control">
<option value="">Select Donation Type</option>
<option value="1">Sadaqa</option>
<option value="2">Fitrana</option>
<option value="3">Zakat</option>
<option value="4">Fidya</option>
<option value="5">Qurbani</option>
<option value="6">Donation</option>
<option value="15">Kaffara</option>
</select>
</div>
</div>
<div class="text-center">
<button type="submit" class="btn btn-lg btn-primary">Submit donation</button>
</div>
</form>
<hr/>
<Section2 />
</div>
</>
)
};
}
export default App1;
I did somthing similar a while ago, you can display the first dropdown and onChange of the first dropdown trigger the API call to update the state to hold the options for the second dropdown, and in the second dropdown section when the api results are empty dont render the second dropdown (could use a (results) ?? <GoodComponent/> will only show if results not empty)
could also add a loading state value to disable the dropdowns in between API calls and listen to its changes using a useEffect hook
I am very new to React and JS. So inside this jsx, I have this this.props.searchresult array, and every element is an object. So I use the map function to create a li element for every object, and also inside the li there is a select tag, which has multiple option tags. I want to use the event attribute function onChange() for select.
But the problem is, how can I pass this book value to the event trigger function?
{this.props.searchresult.map((book) => (
<li key={book.id}>
<div className="book">
<div className="book-top">
<div
className="book-cover"
style={{
width: 128,
height: 193,
backgroundImage: `url(${book.imageLinks.thumbnail})`
}}
/>
<div className="book-shelf-changer">
***<select onChange={this.updateBookStatus}>***
<option value="move" disabled>Move to...</option>
<option value="currentlyReading">Currently Reading</option>
<option value="wantToRead">Want to Read</option>
<option value="read">Read</option>
<option value="none">None</option>
</select>
</div>
</div>
<div className="book-title">{book.title}</div>
<div className="book-authors">{book.authors}</div>
</div>
</li>
))}
By the way, my event trigger function is
updateBookStatus = (event) => {
const val = event.target.value;
// const bookObj = {
// id: book.id,
// imageURL: book.imageLinks.thumbnail,
// title: book.title,
// authors: book.authors
// };
// this.props.moveToReading(bookObj);
console.log(`The value is ${val}`);
// console.log(`The current book is ${book}`)
}
Try this
updateBookStatus = (event, book) => {
// your code here
}
and on your component
<select onChange={event => this.updateBookStatus(event, book)}>
I just create a function that accepts 2 arguments, and send the value of event and book as a parameter.
For passing the book value and evens to updateBookStatus function use the ES6 property arrow function, Please refer the code below
<select onChange={(event) => this.updateBookStatus(event, book)}>
<option value="move" disabled>Move to...</option>
<option value="currentlyReading">Currently Reading</option>
<option value="wantToRead">Want to Read</option>
<option value="read">Read</option>
<option value="none">None</option>
</select>
Here {(event) => this.updateBookStatus(event, book)} updateBookStatus has two arguments one is one event and another one is book value .
The event arguments can be used to get the selected item event.target.value
My render is below. I have run out of ideas.
class Settings extends Component {
render(){
return(
<div className='App container'>
<h3>Select Currency</h3>
<select className="custom-select"
onChange={(prop, attr) => this.props.fetchData(id,value)}>
<option value='selected'></option>
<option value='EUR'>EUR</option
</select>
<Link to='/' className="btn" type="submit">Submit</Link>
</div>
)
}
}
}
Please help, what is wrong with my code? How can I solve this strange error?
Your onChange handler incorrect because no id and value is passed to it, only event object. From event.target object you should get this values, here is example, and short refactor of your component:
class Settings extends Component {
handleChange(e) {
this.props.fetchCurrency(e.target.id, e.target.value)
}
render(){
return(
<div className='App container'>
<h3>Select Currency</h3>
<select className="custom-select" id="inputGroup"
onChange={this.handleChange.bind(this)}>
<option value='selected'></option>
<option value='EUR'>EUR</option>
<option value='AUD'>AUD</option>
<option value='GBP'>GBP</option>
<option value='CAD'>CAD</option>
<option value='JPY'>JPY</option>
</select>
<Link to='/' className="btn btn-light btn-block" type="submit">Submit</Link>
</div>
)
}
}
onChange does by default callback with event (if you log id you'll see a Proxy object). So what you need to do is:
{(event) => this.props.fetchCurrency(event.target.value, event.target.options[event.target.options.selectedIndex].id)}
event.target.options[event.target.options.selectedIndex].id is needed if you want the selected option's ID - which for the time being seems absent in your code.
I have created a custom Select in react. Whenever I try to select a value from the options it reflects old value only e.g I selected 4 from the dropdown handleChange functions will get the value 1 from e.target.value but the dropdown box will show the changed value '4'.
module.exports = React.createClass({
getInitialState:function(){
return {selectValue:'1'};
},
handleChange:function(e){
this.setState({selectValue:e.target.value});
this.props.callbackParent(this.state.selectValue);
},
render: function() {
return (
<div>
<select value={this.state.selectValue}
onChange={this.handleChange} >
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select>
</div>
);
}
});
You are trying get value(selectValue) from state(this.state) whereas it is not completed, in this case you can use callback as a second argument in setState, like this
handleChange: function(e) {
this.setState({selectValue:e.target.value}, function () {
this.props.callbackParent(this.state.selectValue);
});
}
Example
The second (optional) parameter is a callback function that will be
executed once setState is completed and the component is re-rendered.
This works correctly except the type error for props.callbackParent:
https://jsfiddle.net/reactjs/69z2wepo/
HTML:
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
JS:
var Hello = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
JsFiddle : https://jsfiddle.net/69z2wepo/9956/
I am returning a select element in the render function in my react.js code.
But whenever I change the select value, the function in the onChange is not getting triggered.
var Hello = React.createClass({
render: function() {
return <select id="data-type" onChange={changeDataType()}>
<option selected="selected" value="user" data-type="enum">User</option>
<option value="HQ center" data-type="text">HQ Center</option>
<option value="business unit" data-type="boolean">Business Unit</option>
<option value="note" data-type="date">Try on </option>
<option value="con" data-type="number">Con</option>
</select>
}
});
React.render(<Hello/>, document.getElementById('container'));
function changeDataType() {
console.log("entered");
}
This function is getting triggered only once when the select is loaded and subsequently when I change the value, its not getting triggered.
onChange takes a function.
You are passing it changeDataType(), which is running the function changeDataType function, and setting onChange to it's return value, which is null.
Try passing the actual function, instead of evaluating the function.
<select id="data-type" onChange={changeDataType}>
Functions that trigger when a component changes should really be defined inside the component itself.
I recommend defining your function inside of your Hello component, and passing this.changeDataType to your onChange attribute, like so:
var Hello = React.createClass({
changeDataType: function() {
console.log('entered');
},
render: function() {
return <select id="data-type" onChange={this.changeDataType}>
<option selected="selected" value="user" data-type="enum">User</option>
<option value="HQ center" data-type="text">HQ Center</option>
<option value="business unit" data-type="boolean">Business Unit</option>
<option value="note" data-type="date">Try on </option>
<option value="con" data-type="number">Con</option>
</select>
}
});
Here is an updated JSFiddle that produces your expected behavior: https://jsfiddle.net/69z2wepo/9958/
Try this
<select id="data-type" onChange={()=>changeDataType()}>