如何将自定义原生JavaScript下拉菜单适配到React项目中使用
问题核心原因
你参考的原生JS实现是直接操作真实DOM完成交互:包括获取DOM节点、直接修改节点样式类名、给节点绑定原生点击事件,这套逻辑和React的状态驱动视图机制完全不兼容。React会根据组件状态自动重渲染DOM,你手动改的DOM属性、绑定的事件会在重渲染时被覆盖,所以代码跑不起来,不是语法小问题,是实现思路要转成React的状态驱动模式。
React实现可自定义样式下拉选择器(适配搜索栏查询选择场景)
下面是对齐参考效果的React实现,无额外依赖,样式支持完全自定义:
- 核心逻辑用React状态控制下拉展开/收起、选中项,不需要手动操作DOM
- 自带点击组件外部自动收起下拉的逻辑
- 选中值可以直接透传给搜索栏组件作为查询参数
完整组件代码
import { useState, useRef, useEffect } from 'react'; import './CustomSelect.css'; // 选项数据可根据搜索场景自定义,比如搜全站/搜文章/搜用户 const selectOptions = [ { value: 'all', label: '全部范围' }, { value: 'article', label: '文章内容' }, { value: 'user', label: '用户账号' }, { value: 'comment', label: '评论内容' } ]; export default function CustomSelect({ onChange }) { const [isOpen, setIsOpen] = useState(false); const [selected, setSelected] = useState(selectOptions[0]); const selectRef = useRef(null); // 点击组件外部自动收起下拉 useEffect(() => { function handleClickOutside(e) { if (selectRef.current && !selectRef.current.contains(e.target)) { setIsOpen(false); } } document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []); const handleSelect = (option) => { setSelected(option); setIsOpen(false); onChange?.(option.value); // 将选中值传给父级搜索栏组件 }; return ( <div className="custom-select" ref={selectRef}> {/* 选中项触发按钮 */} <div className={`select-trigger ${isOpen ? 'active' : ''}`} onClick={() => setIsOpen(!isOpen)} > {selected.label} </div> {/* 下拉选项列表 */} {isOpen && ( <div className="select-options"> {selectOptions.map(option => ( <div key={option.value} className={`option-item ${selected.value === option.value ? 'selected' : ''}`} onClick={() => handleSelect(option)} > {option.label} </div> ))} </div> )} </div> ); }
对应样式文件CustomSelect.css,可直接修改类名规则调整外观,和参考的原生方案视觉效果一致:
.custom-select { position: relative; width: 140px; font-size: 14px; user-select: none; } .select-trigger { padding: 8px 12px; border: 1px solid #e5e7eb; border-radius: 6px 0 0 6px; background: #fff; cursor: pointer; display: flex; align-items: center; justify-content: space-between; } .select-trigger::after { content: ''; width: 0; height: 0; border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid #666; margin-left: 8px; } .select-trigger.active { border-color: #2563eb; } .select-options { position: absolute; top: calc(100% + 4px); left: 0; width: 100%; border: 1px solid #e5e7eb; border-radius: 6px; background: #fff; box-shadow: 0 4px 12px rgba(0,0,0,0.08); z-index: 100; max-height: 240px; overflow-y: auto; } .option-item { padding: 8px 12px; cursor: pointer; } .option-item:hover { background: #f3f4f6; } .option-item.selected { background: #eff6ff; color: #2563eb; }
组件使用方式
直接放到搜索栏组件中即可,拿到选中的查询范围值后,和搜索关键词拼接发起查询请求:
function SearchBar() { const [searchType, setSearchType] = useState('all'); const [keyword, setKeyword] = useState(''); const handleSearch = () => { // 此处写搜索请求逻辑,searchType为下拉选择的查询范围 console.log('搜索类型:', searchType, '搜索关键词:', keyword); }; return ( <div className="search-bar" style={{display: 'flex'}}> <CustomSelect onChange={(val) => setSearchType(val)} /> <input placeholder="输入搜索内容" value={keyword} onChange={(e) => setKeyword(e.target.value)} style={{flex: 1, padding: '8px 12px', border: '1px solid #e5e7eb', borderLeft: 'none', outline: 'none'}} /> <button onClick={handleSearch} style={{padding: '8px 16px', background: '#2563eb', color: '#fff', border: 'none', borderRadius: '0 6px 6px 0', cursor: 'pointer'}}> 搜索 </button> </div> ); }
注意不要在React组件里直接照搬原生JS的DOM操作代码:比如直接给元素加onclick属性、用getElementsByClassName查找元素修改样式,这类操作会被React的虚拟DOM重绘覆盖,所有视图变化都通过state控制就不会出现逻辑异常。
内容的提问来源于stack exchange,提问作者Blake Dodgen
相关产品推荐
相关产品推荐

