I have a list of products and I want the user to be able to choose one or more from the list.
If I click/select a product for the first time, the console.log
shows the right outcome. However, if I click two times or more, I get the error:
TypeError: Cannot read property 'value' of null
I have tried two different strategies but both are failing (check the function addSelectedProducts
):
First solution
function SearchProductForm() {
const [selectedProducts, setSelectedProducts] = React.useState([]);
function handleSubmit(event){
event.preventDefault();
}
function addSelectedProducts(event) {
console.log(event.target.value)
setSelectedProducts(oldArray => [...oldArray, event.target.value]);
console.log(selectedProducts)
}
return (
<div>
<Form>
<Form.Group controlId="exampleForm.ControlSelect2">
<Form.Label>Select the product(s) you are interested about:</Form.Label>
<Form.Control as="select" multiple onChange={(event) => addSelectedProducts(event)}>
<option value="product1">product1</option>
<option value="product2">product2</option>
<option value="product3">product3</option>
<option value="product4">product4</option>
<option value="product5">product5</option>
</Form.Control>
</Form.Group>
<Button variant="primary" type="submit" onClick={()=>handleSubmit()}>
Submit
</Button>
</Form>
</div>
);
}
export default SearchProductForm;
Second solution
function SearchProductForm() {
const [selectedProducts, setSelectedProducts] = React.useState([]);
function handleSubmit(event){
event.preventDefault();
}
function addSelectedProducts(event) {
let options = event.target.options
for (var i = 0, l = options.length; i < l; i++) {
if (options[i].selected) {
setSelectedProducts(oldArray => [...oldArray, event.target.value]);
console.log(selectedProducts)
}
}
}
return (
<div>
<Form>
<Form.Group controlId="exampleForm.ControlSelect2">
<Form.Label>Select the product(s) you are interested about:</Form.Label>
<Form.Control as="select" onChange={(event) => addSelectedProducts(event)} multiple>
<option value="product1">product1</option>
<option value="product2">product2</option>
<option value="product3">product3</option>
<option value="product4">product4</option>
<option value="product5">product5</option>
</Form.Control>
</Form.Group>
<Button variant="primary" type="submit" onClick={()=>handleSubmit()}>
Search
</Button>
</Form>
</div>
);
}
Ricardo touched on event pooling in their answer but I want to propose a solution where you don't need to persist the event, that's a bit code-smelly for me.
You can grab all the selected options at once and then set them instead of trying to merge new state with old state.
function addSelectedProducts(event) {
// Alternative if you need to target ie
// const selectedOptions = [...event.target.options].filter(o => o.selected).map(o => o.value)
const selectedOptions = [...event.target.selectedOptions].map(o => o.value)
setSelectedProducts(selectedOptions)
}
The reason you're encountering the error above, is that
setSelectedProducts(oldArray => [...oldArray, event.target.value]);
is async and by the time the callback is invoked, your event is no longer around. See more here: https://reactjs.org/docs/events.html#event-pooling
Hi have you checked the docs ? Maybe you have to add event.persist()
cause you are handling the event
inside a function
.
function addSelectedProducts(event) {
event.persist()
console.log(event.target.value)
setSelectedProducts(oldArray => [...oldArray, event.target.value]);
console.log(selectedProducts)
}
Other solution can involve setting a variable to the event.target.value
:
function addSelectedProducts(event) {
let value = event.target.value;
console.log(event.target.value)
setSelectedProducts(oldArray => [...oldArray, event.target.value]);
console.log(selectedProducts)
}
I think you can try onChange={addSelectedProducts}
instead of onChange={(event) => addSelectedProducts(event)}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.