I want to select the option in the select element after all the options are drawn using a map method. I don't want to use setTimeout to wait for a while. I want to know after all the options are rendered.
The select is in a pop up modal which is shown after a button is clicked.
<select
ref={classSelRef}
className="form-select mr-sm-2"
id="class"
required
onChange={e => setClassId(e.target.value)}>
{classes.map(c => {
return (
<option key={c.id} value={c.id}>
{c.class_name}
</option>
)
})}
</select>
And here is the option selection method.
const selectOption = (ele, value) => {
return [...ele.options].some((option, index) => {
if (option.value === value) {
ele.selectedIndex = index
return true
}
})
}
You can add logical operator && before map function like so
<select
ref={classSelRef}
className="form-select mr-sm-2"
id="class"
required
onChange={e => setClassId(e.target.value)}>
{classes && classes.map(c => {
return (
<option key={c.id} value={c.id}>
{c.class_name}
</option>
)
})}
This will wait classes to be true before map is executed
Related
I'm building the website but I got error in the browser's console, what's the problem and how to fix it. And another promblem is I can not get value from first and second select but input is fine.
Here's the code.
const Refund = () => {
const [payment, setPayment] = useState({
rpayment: ""
});
const [reason, setReason] = useState({
rrefund: ""
});
const [date, setDate] = useState({
ddate: ""
});
const handleInputOnChange = ({ currentTarget: select, input}) => {
const tempPayment = {...payment};
tempPayment[select.id] = select.value;
setPayment(tempPayment);
console.log(tempPayment)
const tempReason = {...reason};
tempReason[select.id] = select.value;
setReason(tempReason);
console.log(tempReason)
const tempDate = {...date};
tempDate[input.id] = input.value;
setDate(tempDate);
console.log(tempDate)
};
return (
<div className="refund">
<fieldset className="refund__container">
<legend align="center">
</legend>
<form className="refund__form">
<div className="refund__form__container">
<div className="refund__form__item" >
<select name="rpayment" id="rpayment" value={payment["rpayment"]} onChange={handleInputOnChange}>
<option value="none" selected disabled hidden>ช่องทางการรับเงิน</option>
<option value={payment["rpayment"]}>Credit Card</option>
<option value={payment["rpayment"]}>Paypal</option>
<option value={payment["rpayment"]}>Wallet</option>
</select>
</div>
<div className="refund__form__item">
<input
type="date"
id="rdate"
name="rdate"
value={reason["ddate"]}
onChange={handleInputOnChange}
/>
</div>
<div className="refund__form__item">
<select name="reason" id="reason" value={reason["rrefund"]} onChange={handleInputOnChange}>
<option value="none" selected disabled hidden>เหตุผลที่ต้องการเงินคืน</option>
<option value={reason["rrefund"]}>I need to cancle</option>
<option value={reason["rrefund"]}>It's not comfortable</option>
<option value={reason["rrefund"]}>Too expensive</option>
</select>
</div>
</div>
</form>
<input type="submit" className="refund__btn" value="Submit" />
</fieldset>
</div>
)
}
export default Refund;
Thank you in advance!!
There's such a huge amount of problems that I don't even know which one to start from.
Well let's start with state. One state is enough.
const [formState, setFormState] = useState({
rpayment: "none", // refers to paymentMethods items
rrefund: "expensive", // refers to refundReasons value field
ddate: "" // here's input string
})
Second - selects and their change handlers.
// simple approach
const paymentMethods = ['none', 'Credit', 'Paypal', 'Wallet'];
// complex approach
const refundReasons = [
{
title: 'I need to cancle',
value: 'need'
},
{
title: 'Too expensive',
value: 'expensive'
}
// add some more
]
Select handler. Here we are currying function and returning a new one with enclosed stateFieldName referring to our composed state field.
But I'm not actually sure if we must use event.target or event.currentTarget - try both, one will work for sure.
const selectHandler = (stateFieldName) => (event) => {
setFormState({
...formState,
[stateFieldName]: event.currentTarget.value
})
}
Render selects (no styles, sorry)
// no need to apply hidden fields
<select
onChange={selectHandler('rpayment')} // yes, this is a function call, it returns a new function
>
{paymentMethods.map((item) => (
<option
key={item}
value={item}
selected={item === formState.rpayment}
>
{item}
</option>
))
}
</select>
// complex select
<select
onChange={selectHandler('rrefund')}
>
{refundReasons.map(({title, value}) => (
<option
key={value}
value={value}
selected={value === formState.rrefund}
>
{title}
</option>
))}
</select>
// input
<input
type="date"
value={formState.ddate}
onChange={selectHandler(ddate)} // probably this handler will work here too
/>
And finally submit button. If your form is supposed to send async request, your submit button doesn't need value prop and any handler. I general it must have type='submit' prop but the form itself must have a submit hander and send data stored in your state.
Something like
<form
onSubmit={() => {
axios.post('/some/api', {
data: formState
})
.then(/* you logic here */)
}}
>
// your inputs here
</form>
Sorry for approximations and possible mistakes, I hope you got the main idea.
I have a select such as :
<Select id='1' list={[...this.state.dictionnary]}/>
where this.state.dictionnary is like this :
state = {
dictionnary: [
{value: "a", name: "ab"},
]}
And the component select is like this :
class Select extends Component {
handleChange()
{
// I would like to show 1, a and ab
// 1 is the id of the select
// a and ab are value and name of
};
render() {
return (
<select onChange={this.handleChange} className="custom-select">{this.props.list.map(option => (
<option key={option.value} value={option.value}>{option.name}</option>))}
</select>
)
}
}
export default Select;
I would like to show some informations using the handleChange() function like the id, name and value.
Could you help me please ?
Thank you very much
You should change the option so the value has the entire object to get it later in the handleChange. Also, to access the same this with the props in the handleChange method you need to use an arrow function:
<select onChange={(e) => this.handleChange} className="custom-select">
{this.props.list.map((option) => (
<option key={option.value} value={option}>
{option.name}
</option>
))}
</select>
By default, the onChange handler gets the event param so you can get the target and the props:
handleChange(event) {
const { value, name } = event.target.value;
console.log(this.props.id, value, name);
}
Adding to #Alavaro answer, you need to split after getting the value to remote ,
handleChange = e => {
const [value, name] = e.target.value.split(',')
console.log({ id: this.props.id, value, name})
}
render() {
return (
<select onChange={this.handleChange} className="custom-select">
{this.props.list.map((option, index) => (
<option key={option.value} value={[option.value, option.name]} >
{ option.name }
</option>
))}
</select>
);
}
}
I've got 2 selects in my form. Options for the 2nd select are generated based on 1st select. Everything works fine except that every time I choose an option for the 2nd select and then change the 1st one, I should get the default option for the 2nd, but it's not resetting.
Code below
const [animalClass, setAnimalClass] = useState();
const [animalType, setAnimalType] = useState();
const [isLoadingClasses, setLoadingClasses] = useState(true);
const [isLoadingTypes, setLoadingTypes] = useState(true);
const [availableAnimalClasses, setAvailableAnimalClasses] = useState();
const [availableAnimalTypes, setAvailableAnimalTypes] = useState();
useEffect(() => {
setLoadingClasses(true);
const availableOptions = async () => {
const availableClasses = await Axios.get();
console.log(availableClasses.data);
if (availableClasses.data.length > 0) {
setAvailableAnimalClasses(availableClasses.data.map(animal => ({name: animal.name})));
setLoadingClasses(false);
}
};
availableOptions();
}, []);
useEffect(() => {
setLoadingTypes(true);
setAnimalType("DEFAULT");
const availableOptions = async () => {
const availableTypes = await Axios.get();
console.log(availableTypes.data);
if(availableTypes.data.length > 0) {
setAvailableAnimalTypes(availableTypes.data.map(animal => ({name: animal.name})));
setLoadingTypes(false);
}
};
availableOptions();
}, [animalClass]);
return (
<div className="page">
<h2>New Auction</h2>
<form className="form" onSubmit={onSubmit}>
<label>
Animal Class
<select defaultValue={'DEFAULT'} onChange={(e) => setAnimalClass(e.target.value)} >
<option value="DEFAULT" disabled>Choose animal class</option>
{isLoadingClasses ? <option value="Loading" disabled>Loading.....</option> : availableAnimalClasses.map((option) => (
<option value={option.name}>{upperCase(option.name)}</option>
))}
</select>
</label>
<label>Animal Type
<select defaultValue={'DEFAULT'} onChange={(e) => setAnimalType(e.target.value)} >
<option value="DEFAULT" disabled>Choose animal type</option>
{isLoadingTypes? <option value="Loading" disabled>Loading.....</option> : availableAnimalTypes.map((option) => (
<option value={option.name}>{upperCase(option.name)}</option>
))}
</select>
Since you are using state to store your form field values, you could set your defaults when you create the hooks:
const [animalClass, setAnimalClass] = useState('DEFAULT');
const [animalType, setAnimalType] = useState('DEFAULT');
and then use value instead of defaultValue in your JSX:
<select value={animalClass} onChange={(e) => setAnimalClass(e.target.value)} >
...
<select value={animalType} onChange={(e) => setAnimalType(e.target.value)} >
These values should reset your select elements when the state changes.
You should call setAnimalType to reset it back to the default when selecting the animal class. Change:
<select defaultValue={'DEFAULT'} onChange={(e) => setAnimalClass(e.target.value)}>...</select>
...to:
<label>
Animal Class
<select
defaultValue={'DEFAULT'}
value={animalClass}
onChange={(e) => {
setAnimalClass(e.target.value);
setAnimalType('DEFAULT');
}}
>
...
</select>
</label>
<label>
Animal Type
<select
defaultValue={'DEFAULT'}
value={animalType}
onChange={(e) => setAnimalType(e.target.value)}
>
// ...
</select>
</label>
This is necessary because the onChange event will not be automatically fired for a select when the options change. In order for this to work, where state controls the state of your <select/>s, you'll also want to change the defaultValueprops tovalue` props.
I have a select and it gets the options from the api, and it looks like this:
I want to sort them in ascending form but I don't know how to do that, here is my code
<select className="form-control form-control-sm" name="subscriptions" onChange={this.changeValues}>
{
this.state.variantsData.subscription_plans.map((item, i) => <option key={i} value={item.uuid}>{item.name}</option>)
}
</select>
You could write a function that sorts your data which you can then render. To optimise it further you can memoize this function
sortData = (array) => {
return array.sort(function(a, b) {
a.match(/(\d+) months/i)[1] - b.match(/(\d+) months/i)[1]
})
}
render() {
const data = this.sortData(this.state.variantsData.subscription_plan);
return (
<select className="form-control form-control-sm" name="subscriptions" onChange={this.changeValues}>
{this.state.variantsData.subscription_plans.map((item, i) => (
<option key={i} value={item.uuid}>{item.name}</option>
))
}
</select>
)
}
I have three different selections and I want to store each selected and current option its own state. I also want when the user changes option to store that on the state.
Here is my states:
this.state = {
header: [
'genre',
'sold',
'income'
],
chartData: [
'Line',
'Bar',
'AreaChart',
'PieChart',
'ScatterChart'
],
axisX: '',
axisY: '',
selectedLine: ''
}
Here is my "handleChart" function. Basically what I am trying to do here is, when the component loads I want to store whatever current options on the state and when the user changes or selects new option, I want to store that on the state too.
handleChart = (e) => {
e.preventDefault();
const value = e.target.value;
if(value === 'line'){
// First selection: contains >>> different chart names
this.setState({selectedLine: value})
}else if(value === 'genre'){
// Second selection: contains >>> different headers
this.setState({axisX: value})
}else if(value === 'income'){
// Third selection: contains >>> different headers
this.setState({axisY: value)
}
}
My App.js
<div className="App">
<div style={{display: 'inline-block'}}>
<span>Chart type: </span>
<select onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.chartData.map((k, v) => (
<option name="line" key={k} value="line">{k}</option>
))}
</select>
<span>x-axis: </span>
<select onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.header.map((k, v) => (
<option name="xaxis" key={k} value={k}>{k}</option>
))}
</select>
<span>y-axis: </span>
<select onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.header.map((k, v) => (
<option name="yaxis" key={k} value={k}>{k}</option>
))}
</select>
</div>
</div>
It looks like you already have a way to update state based on changing the value of an input, however your current approach can be improved to be more robust. You can give each input a name attribute and then have the name correspond to the variable name within state so you don't need a bunch of if/else if blocks.
To have inputs take values from state, set the value attribute on the input/select/etc (rather than on option) equal to the value from state that you want to use.
Here is an updated version of handleChart:
handleChart = event => {
event.preventDefault();
const { name, value } = event.target;
this.setState({
[name]: value;
});
}
And updated App.js:
render() {
const { axisX, axisY, selectedLine } = this.state;
return (
<div className="App">
<div style={{display: 'inline-block'}}>
<span>Chart type: </span>
<select name="selectedLine" value={ selectedLine } onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.chartData.map((k, v) => (
<option name="line" key={k} value={v}>{v}</option>
))}
</select>
<span>x-axis: </span>
<select name="axisX" value={ axisX } onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.header.map((k, v) => (
<option name="xaxis" key={k} value={v}>{v}</option>
))}
</select>
<span>y-axis: </span>
<select name="axisY" value={ axisY } onChange={this.handleChart} style={{marginRight: '15px'}}>
{this.state.header.map((k, v) => (
<option name="yaxis" key={k} value={v}>{v}</option>
))}
</select>
</div>
</div>
)
}