React: создание поля со списком

Студент здесь, пытается создать приложение для своей диссертации, используя Laravel, React и Inertia.

Я пытаюсь создать простой компонент Combobox многократного использования. Он должен иметь HTML-атрибут id и содержать идентификатор в качестве значения, чтобы я мог использовать и то, и другое в контроллере Laravel.

Вот что у меня есть. Проблема в том, что большинство строк функции handleSelect не работают. (С пометкой «не бывает».)

Когда пользователь выбирает элемент из ul, я ожидаю:

  • видимый ввод {title}_input, содержащий item.name ->, не происходит
  • скрытый {title}_id, содержащий item.id ->, не происходит
  • ул, которую нужно закрыть -> БЫВАЕТ

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.info(`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>

🤔 А знаете ли вы, что...
React - это библиотека JavaScript, разработанная компанией Facebook.


181
2

Ответы:

Попробуй это

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

или

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



   

Решено

Проблема в том, что большинство строк функции handleSelect не работают.

Функция не вызывается. Так что на самом деле это все строки кода в этой функции, и дело не в том, что они «не работают», а в том, что они просто не вызываются вообще.

Это событие происходит первым:

onBlur = {() => {setShowUl(false)}}

Фактически это означает повторный рендеринг компонента до того, как событие click будет обработано. Удалите его, и событие click заработает как положено:

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.info(`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>

Скрыть меню — это немного сложнее, чем просто событие onBlur для элемента <input>. Такой инструмент, как этот может быть тем, что вы ищете, или, возможно, создайте свой собственный. Но по сути вам нужно скрыть <ul> при нажатии в любом месте, кроме <input> или <ul>.