Student here, trying to create an app for my thesis, using Laravel, React and Inertia.
I am trying to create a simple, reusable Combobox component. It needs to have an id HTML attribute and it needs to contain an id as a value, so that I can use both in a Laravel controller.
This is what I've got. Problem is that most of the lines of the handleSelect function don't work. (Marked with "does not happen".)
When the user selects an item from the ul, I expect:
const {useState} = React;
const Combobox = ({}) => {
const [input, setInput] = useState('')
const [returnValue, setReturnValue] = useState ('')
const [showUl, setShowUl] = useState(false)
//TURN INTO PROPS
const title = 'test'
const array = [
{ id: 1, name: 'eins' },
{ id: 2, name: 'zwei' },
{ id: 3, name: 'polizei' },
]
//end of props
const filteredArray = (
input
? array.filter(item => item.name.toLowerCase().includes(input.toLowerCase()))
: array
)
const handleSelect = (name, id) => {
setInput(name) // does not happen.
setReturnValue(id) // does not happen.
setShowUl(false)
console.log(`input is now ${input}`) // does not happen.
}
return (
<div>
<input
id={`${title}_input`}
type="text"
placeholder='choose an option'
value={input}
onChange={e => {setInput(e.target.value)}}
onFocus={() => {setShowUl(true)}}
onBlur={() => {setShowUl(false)}}
/>
<input
id={`${title}_id`}
type="hidden"
value={returnValue}
/>
{showUl &&
<ul>
{filteredArray.map(item => (
<li
key={item.id}
onClick={() => handleSelect(item.name, item.id)}
>{item.name}</li>
))}
</ul>
}
</div>
)
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Combobox/>
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
Problem is that most of the lines of the handleSelect function don't work.
The function isn't being called. So it's really all of the lines of code in that function, and it's not that they "don't work" but rather that they're simply not being invoked in the first place.
This event is occurring first:
onBlur={() => {setShowUl(false)}}
Which is effectively re-rendering the component before that click
event is ever processed. Remove it and the click
event works as expected:
const {useState} = React;
const Combobox = ({}) => {
const [input, setInput] = useState('')
const [returnValue, setReturnValue] = useState ('')
const [showUl, setShowUl] = useState(false)
//TURN INTO PROPS
const title = 'test'
const array = [
{ id: 1, name: 'eins' },
{ id: 2, name: 'zwei' },
{ id: 3, name: 'polizei' },
]
//end of props
const filteredArray = (
input
? array.filter(item => item.name.toLowerCase().includes(input.toLowerCase()))
: array
)
const handleSelect = (name, id) => {
setInput(name) // does not happen.
setReturnValue(id) // does not happen.
setShowUl(false)
console.log(`input is now ${input}`) // does not happen.
}
return (
<div>
<input
id={`${title}_input`}
type="text"
placeholder='choose an option'
value={input}
onChange={e => {setInput(e.target.value)}}
onFocus={() => {setShowUl(true)}}
/>
<input
id={`${title}_id`}
type="hidden"
value={returnValue}
/>
{showUl &&
<ul>
{filteredArray.map(item => (
<li
key={item.id}
onClick={() => handleSelect(item.name, item.id)}
>{item.name}</li>
))}
</ul>
}
</div>
)
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Combobox/>
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
To hide the menu is a bit more involved than just an onBlur
event for the <input>
element. A tool like this one may be what you're looking for, or perhaps creating your own. But essentially you'd want to hide the <ul>
when clicking anywhere except the <input>
or the <ul>
.