Clear a specific select in react-bootstrap - javascript

I need to clear the second select if it already has a selected value, in case the first one selects B.
When cleaning it requires that the value be removed, disabled and that it has the Select... option by default.
import React, { useState } from "react";
import { Form, Col, Button } from "react-bootstrap";
const App = () => {
const [data, setData] = useState({
to: "",
from: "",
});
const handleChange = (e) => {
const { name, value } = e.target;
setData((prevState) => ({
...prevState,
[name]: value,
}));
};
return (
<Form>
<Form.Row>
<Form.Group as={Col} md="auto">
<Form.Label>to</Form.Label>
<Form.Control
required
name="to"
as="select"
placeholder="to"
onChange={handleChange}
>
<option hidden value="" selected>
Select...
</option>
<option value="A">A</option>
<option value="B">B</option>
</Form.Control>
</Form.Group>
<Form.Group as={Col} md="auto">
<Form.Label>from</Form.Label>
<Form.Control
required
name="from"
as="select"
placeholder="from"
onChange={handleChange}
>
<option hidden value="" selected>
Select...
</option>
<option value="A">A</option>
<option value="B">B</option>
</Form.Control>
</Form.Group>
<Button variant="primary">Ok</Button>
</Form.Row>
</Form>
);
};
export default App;
How can I modify the code above to do what I commented? Thanks!

what you have here is cascading issue your second select depends on the first select. In your handle change you can do this
const handleChange = (e) => {
const { name, value } = e.target;
if(name === 'to' && data.from){
setData((prevState) => ({
...prevState,
[name]: value,
from: ''
}));
} else {
setData((prevState) => ({
...prevState,
[name]: value,
}));
}
};
We are checking whether the value we are changing is the first select and also if the second select already has a value . If yes then we are clearing the value in the second select.
Also since you are using the state to preserve the form values , you need to add the value prop to your component to make it as Controlled Component .
<Form.Control
required
name="to"
as="select"
placeholder="to"
value={data.to} // add this prop
onChange={handleChange}
>
<option hidden value="">
Select...
</option>
<option value="A">A</option>
<option value="B">B</option>
</Form.Control>

Related

How to wait for options of a select element

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

"TypeError: Cannot read properties of undefined (reading 'id')"

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.

Update a select's options based on another dropdown, using React

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.

How to handle multiple select dropdowns in a React application with a single function?

I have a forms with 3 select dropdowns that filter an array based on a selected value. Rather than having an onChange handler for each of these forms I was wondering if I could use a single function too handle all of them based on a parameter.
I tried passing a string into the function but it doesn't work since it is expecting an event to be passed as a parameter.
Here is the code for the react form:
const UserSearchBox = ({ handleChange, handleLocationChange, handleTeamChange, handleClientChange }) => {
return (
<form className="user-search">
<input className="user-input" placeholder="Search for Peeps" name="userSearch" onChange={handleChange} />
<div>
<select defaultValue="all" onChange={handleLocationChange}>
<option value="all">All</option>
<option value="boston">Boston</option>
<option value="chicago">Chicago</option>
<option value="tampa">Tampa</option>
</select>
<select defaultValue="all" onChange={handleTeamChange}>
<option value="all">All</option>
<option value="design">Design</option>
<option value="engineering">Engineering</option>
<option value="finance">Finance</option>
</select>
<select defaultValue="all" onChange={handleClientChange}>
<option value="all">All</option>
<option value="company">Company1</option>
<option value="company2">Company2</option>
<option value="company3">Company3</option>
</select>
</div>
</form>
)
}
And this is the code for the App:
const handleInputChange = (e) => {
e.preventDefault();
let searchTerm = e.target.value;
if(searchTerm){
let filteredUsers = users.filter((x) => x.name.toLowerCase().includes(searchTerm.toLowerCase()))
setUsers(filteredUsers)
} else{
setUsers(allUsers)
}
}
const handleLocationChange = (e) => {
e.preventDefault();
let searchTerm = e.target.value;
if(searchTerm !== "all"){
let filteredUsers = allUsers.filter((x) => x.location.toLowerCase().includes(searchTerm.toLowerCase()))
setUsers(filteredUsers)
} else{
setUsers(allUsers)
}
}
return (
<div className="container-fluid">
<Header name={loggedInUser.name} profile_photo={loggedInUser.profile_photo} />
<UserSearchBox handleLocationChange={handleLocationChange} handleChange={handleInputChange}/>
<Users users={users} />
</div>
)
You can pass additional parameters with the event by changing the onChange to this:
onChange={(e) => { handleLocationChange(e, param1, param2, param3...) }}
Or if you dont need the event you can skip it:
onChange{() => { handleLocationChange(param1, param2, param3...) }}
That way you still get your event inside, and then you add whatever other arguments you want.
Would that help you?
Another potential solution, however I believe #Darkbound had a more precise method.
Add data attributes to the options:
<select defaultValue="all" onChange={handleSelectChange}>
<option data-filter-type="location" value="all">All</option>
<option data-filter-type="location" value="boston">Boston</option>
<option data-filter-type="location" value="chicago">Chicago</option>
<option data-filter-type="location" value="tampa">Tampa</option>
</select>
Then use a switch statement:
if (searchTerm !== "all") {
switch (attr) {
case 'location':
setUsers(users.filter((x) => x.location.toLowerCase().includes(searchTerm.toLowerCase())));
break;
case 'team':
console.log('term: ', searchTerm);
setUsers(users.filter((x) => x.team.toLowerCase().includes(searchTerm.toLowerCase())));
break;
case 'client':
setUsers(users.filter((x) => x.client.toLowerCase().includes(searchTerm.toLowerCase())));
break;
default:
setUsers(allUsers)
}
} else {
setUsers(allUsers)
}

on submit button click, execute function then submit form react.js

I have an order form that currently takes a users input and posts it into a dynamo db table on submit.
Right now, that's all the "submit" button does.
I want the on click to instead calculate a price based off of the users input, hide the form, display the price with an "accept and schedule" button, that then posts to dynamo db.
-----------------I am very new to to react js so I apologize if my code is sloppy. -------------------
I figured that my function to handle the calculation would look something like the following, as the price is based off of the property square footage in increments of 500.
let base_price = 149.99;
if(sqft > 2000){
let overage = sqft - 2000;
let percentage = Math.ceil(overage % 500) * 10;
base_price += base_price * percentage;
}
Now here is my current order form code:
import React, { Component } from "react";
import { InputGroup, Row, Form, Col, FormGroup, FormControl, ControlLabel } from "react-bootstrap";
import LoaderButton from "../components/LoaderButton";
import config from "../config";
import { API } from "aws-amplify";
export default class OrderForm extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: null,
streetAddress: "",
streetAddress2: "",
city: "",
state: "",
zipCode: "",
propertySqft: "",
packageSelected: this.props.location.state,
};
}
validateForm() {
return this.state.streetAddress.length > 0;
return this.state.streetAddress2.legnth >= 0;
return this.state.city.length > 0;
return this.state.state.length > 0;
return this.state.zipCode.length > 0;
return this.state.propertySqft.length > 0;
return this.state.packageSelected.length > 0;
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value,
});
}
handleSubmit = async event => {
event.preventDefault();
this.setState({ isLoading: true });
try {
await this.createJob({
streetAddress: this.state.streetAddress,
streetAddress2: this.state.streetAddress2,
city: this.state.city,
state: this.state.state,
zipCode: this.state.zipCode,
propertySqft: this.state.propertySqft,
packageSelected: this.state.packageSelected,
});
this.props.history.push("/Scheduled");
} catch (e) {
alert(e);
this.setState({ isLoading: false });
}
}
createJob(job) {
return API.post("dynamodbname", "/prapp", {
body: job
});
}
render() {
var centerText = {textAlign: "center"}
return (
<div className="NewJob">
<Form onSubmit={this.handleSubmit}>
<Form.Group controlId="packageSelected">
<Form.Label>
</Form.Label>
<InputGroup>
<InputGroup.Prepend>
<InputGroup.Text id="inputGroupPrepend">Package Selected:</InputGroup.Text>
</InputGroup.Prepend>{" "}
<Form.Control style={centerText} onChange={this.handleChange} plaintext readOnly defaultValue={this.props.location.state} />
</InputGroup>
</Form.Group>
<Form.Group controlId="streetAddress">
<Form.Label>Address 1</Form.Label>
<Form.Control onChange={this.handleChange} value={this.state.streetAddress} placeholder="Property Address" />
</Form.Group>
<Form.Group controlId="streetAddress2">
<Form.Label>Address 2</Form.Label>
<Form.Control onChange={this.handleChange} value={this.state.streetAddress2} placeholder="Apartment, studio, or floor" />
</Form.Group>
<Form.Row>
<Form.Group as={Col} controlId="city">
<Form.Label>City</Form.Label>
<Form.Control onChange={this.handleChange} value={this.state.city} placeholder="City" />
</Form.Group>
<Form.Group as={Col} controlId="state">
<Form.Label>State</Form.Label>
<Form.Control onChange={this.handleChange} value={this.state.state} as="select">
<option value="AL">State</option>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AZ">Arizona</option>
<option value="AR">Arkansas</option>
<option value="CA">California</option>
<option value="CO">Colorado</option>
<option value="CT">Connecticut</option>
<option value="DE">Delaware</option>
</Form.Control>
</Form.Group>
<Form.Group as={Col} controlId="zipCode">
<Form.Label>Zip</Form.Label>
<Form.Control onChange={this.handleChange} value={this.state.zipCode} placeholder="Zip Code" />
</Form.Group>
</Form.Row>
<Form.Group controlId="propertySqft">
<Form.Label>Square Feet</Form.Label>
<Form.Control onChange={this.handleChange} value={this.state.propertySqft} placeholder="1234" />
</Form.Group>
<Form.Group id="formGridCheckbox">
<Form.Check type="checkbox" label="I agree to terms and services" />
</Form.Group>
<LoaderButton
block
bsStyle="primary"
bsSize="large"
disabled={!this.validateForm()}
type="submit"
isLoading={this.state.isLoading}
text="Calculate Price"
loadingText="Calculating your price…."
/>
</Form>
</div>
);
}
}
So how would I go about instead the on submit triggers a function that calculates a price based off of the users input, hide the form, display the price with an "accept and schedule" button, and then posts to dynamo db?
If anyone could share either some resources or insight on how I should go about executing this, that would be extremely appreciated!
Images of calculation debugger:
property sqft is 5000
Overage is 3000
Percentage is Zero?
when set to 5001 sqft, percentage is only 10?
In state, have a value called totalPrice, initialized at null, and add a function that get's met before the official onSubmit. It could look something like this.
checkPrice = () =>{
//YOUR PRICE EVAL BASED ON USER INPUT
this.setState({totalPrice: YOUR_PRICE_EVAL})
}
In your render, have a condition that says if totalPrice then show price and two buttons (continue, which will execute form submission, or back, which will perform setState({totalPrice: null}) And reveal the form again.
{this.state.totalPrice ? (
<div>
Your price is {this.state.totalPrice}
<button onClick={this.handleSubmit}>Continue?</button>
<button onClick={()=>{this.setState({totalPrice: null})}}>Back to Eval</button>
</div>
) : (
<Form onSubmit={this.checkPrice}>
...
</Form>
Here is a quick code sandbox. The code probably doesn't match up 100% with yours but it should be enough to point you in the right direction.
Alternatively you can check out Ant Design's Popconfirm component. Basically you wrap it around whatever is going to be clicked (i.e. the submit button) And before it submit's anything the component will render a popup. You can populate the popup with "Your price is..." and calculate the final price. Then pass the props onConfirm={YOUR_SUBMIT_FUNCTION} onCancel={()=>{this.setState({totalPrice: null})}}

Categories

Resources