You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将自定义原生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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:45:41