skip to Main Content

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:

  • the visible input {title}_input to contain item.name -> doesn’t happen
  • the hidden {title}_id to contain item.id -> doesn’t happen
  • the ul to close -> DOES happen
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>

2

Answers


  1. Try this

     onClick={() => this.handleSelect(item.name, item.id)}
    

    or

    onClick={handleSelect(item.name, item.id)}
    
    
    
       
    
    Login or Signup to reply.
  2. 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>.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search