在React-bootstrap Modal中使用useCombobox触发控制台错误的问题
问题场景
在React-Bootstrap的Modal组件内使用downshift的useCombobox钩子时,控制台会抛出以下错误:
downshift: You forgot to call the getInputProps getter function on your component / element.
downshift: You forgot to call the getMenuProps getter function on your component / element.
downshift: The ref prop "undefined" from getInputProps was not applied correctly on your element.
downshift: The ref prop "undefined" from getMenuProps was not applied correctly on your element.
虽然表单功能最终正常运行,但错误会在页面加载时触发。核心原因是React-Bootstrap的Modal默认在隐藏状态下不会渲染内部DOM,此时useCombobox已经完成初始化,但对应的getInputProps、getMenuProps等getter函数并未被调用(因为Modal内的组件还未挂载),downshift的内部检测机制会认为钩子已初始化但必要的props未被应用,从而抛出错误;直到Modal显示、内部组件渲染后,这些getter才被执行,但错误已经输出。
原相关代码如下:
const { getInputProps: getInputPropsTag, getItemProps: getItemPropsTag, getMenuProps: getMenuPropsTag, openMenu: openMenuTag, isOpen: isOpenTag } = useCombobox({ items: __itemsTags, onInputValueChange({ inputValue }) { _handleChangeTag(inputValue) }, onSelectedItemChange: ({ selectedItem: __selectedItem }) => _handleSelectTag(__selectedItem), itemToString: item => (item ? item.value : '') }); <Modal className='_blogModal' show={_showModal} onHide={() => setShowModal(false)} centered> <Modal.Header closeButton> <Modal.Title className='d-flex'> <Dropdown show={_showFilterDropdown} onMouseEnter={() => { setShowFilterDropdown(true); }} onMouseLeave={() => { setShowFilterDropdown(false); }} > <Dropdown.Toggle as='span'></Dropdown.Toggle> <Dropdown.Menu className='border-0 border-top rounded-0'> <Form className='d-flex flex-column'> <Dropdown.Item as='span' eventKey='4'> <Controller name='_tagInput' control={control} render={({ field }) => ( <Form.Group controlId='_tagInput' className={`_formGroup _searchGroup ${_tagFocused || !_.isEmpty(field.value) ? 'focused' : ''}`} > <FloatingLabel label='Tags.' className='_formLabel _autocomplete' > <FontAwesomeIcon icon={faHashtag} /> <Form.Control {...getInputPropsTag({ ...field, onFocus: _handleFocusTag, onBlur: _handleBlurTag })} placeholder='Tags.' autoComplete='new-password' type='text' className={`_formControl border border-0 rounded-0 ${!_.isEmpty(_typedCharactersTag) ? '_typing' : ''}`} name='_tagInput' /> </FloatingLabel> <SimpleBar className='_SimpleBar' style={{ maxHeight: '40vh' }} forceVisible='y' autoHide={false}> <ListGroup className='border border-0 rounded-0 d-block' {...getMenuPropsTag()} > { isOpenTag && _.map( __itemsTags , (item, index) => { return ( <ListGroup.Item className='border border-0 rounded-0 d-flex align-items-center' {...getItemPropsTag({ key: index, index, item })} > <FontAwesomeIcon icon={faHashtag} className='me-2' /> {item.value} </ListGroup.Item> ) } ) } </ListGroup> </SimpleBar> </Form.Group> )} /> </Dropdown.Item> </Form> </Dropdown.Menu> </Dropdown> </Modal.Title> </Modal.Header> </Modal>
解决方案
1. 条件初始化useCombobox
只有当Modal处于显示状态时才初始化useCombobox,避免在Modal未渲染时提前创建钩子实例,从根源上避免downshift的错误检测:
// 仅在Modal显示时初始化useCombobox const comboboxProps = _showModal ? useCombobox({ items: __itemsTags, onInputValueChange({ inputValue }) { _handleChangeTag(inputValue) }, onSelectedItemChange: ({ selectedItem: __selectedItem }) => _handleSelectTag(__selectedItem), itemToString: item => (item ? item.value : '') }) : {}; const { getInputProps: getInputPropsTag, getItemProps: getItemPropsTag, getMenuProps: getMenuPropsTag, openMenu: openMenuTag, isOpen: isOpenTag } = comboboxProps;
2. 确保ref正确传递
在合并getInputProps和React Hook Form的field props时,注意顺序:先展开field,再展开getInputPropsTag的结果,确保downshift返回的ref不会被覆盖,解决"ref prop undefined"的错误:
<Form.Control {...field} {...getInputPropsTag({ onFocus: _handleFocusTag, onBlur: _handleBlurTag })} placeholder='Tags.' autoComplete='new-password' type='text' className={`_formControl border border-0 rounded-0 ${!_.isEmpty(_typedCharactersTag) ? '_typing' : ''}`} name='_tagInput' />
3. 可选:Modal显示后延迟初始化逻辑
如果需要在Modal显示后执行菜单相关的初始化操作,比如自动打开菜单,可以用useEffect监听Modal的显示状态:
import { useEffect } from 'react'; useEffect(() => { if (_showModal && openMenuTag) { // 根据需求执行初始化,比如打开菜单 // openMenuTag(); } }, [_showModal, openMenuTag]);
内容的提问来源于stack exchange,提问作者Zakariae Bou-taleb

