控制台测试React Dropdown代码时遇Cannot read properties of null报错求助
解决Dropdown点击范围判断的控制台错误问题
问题描述
在浏览器控制台测试点击是否在Dropdown组件范围内时,执行以下代码触发错误:
const dropdown = document.querySelector('w-48'); const handleClick = (event) => { if (dropdown.contains(event.target)) { console.log('Inside dropdown'); } else { console.log('OUTSIDE Dropdown'); } } document.addEventListener('click', handleClick, true);
错误信息:
Cannot read properties of null (reading 'contains') TypeError: Cannot read properties of null (reading 'contains') at HTMLDocument.handleClick (:4:16)
对应的React Dropdown组件代码如下:
import { useState } from "react"; import { GoChevronDown, GoChevronLeft } from "react-icons/go"; import Panel from "./Panel"; function Dropdown({ options, value, onChange }) { const [isOpen, setIsOpen] = useState(false); const handleClick = () => { setIsOpen(!isOpen); }; const handleOptionClick = (option) => { // CLOSE DROPDOWN setIsOpen(false); // WHAT OPTION DID THE USER CLICK ON?? onChange(option); }; const renderedOptions = options.map((option) => { return ( <div className='hover:g-sky-100 rounded cursor-pointer p-1' onClick={() => handleOptionClick(option)} key={option.value} > {option.label} </div> ); }); return ( <div className='w-48 relative'> <Panel className='flex justify-between items-center cursor-pointer' onClick={handleClick} > {value?.label || "Select..."} {isOpen ? ( <GoChevronDown className='text-3xl' /> ) : ( <GoChevronLeft className='text-3xl' /> )} </Panel> {isOpen && <Panel className='absolute top-full'>{renderedOptions}</Panel>} </div> ); } export default Dropdown;
预期点击Dropdown时控制台输出inside dropdown,但实际触发错误。
解决方案
1. 修复控制台代码的选择器错误
document.querySelector('w-48')写法错误,w-48是CSS类名,选择类名需要加.前缀,正确写法:
const dropdown = document.querySelector('.w-48');
如果页面中有多个w-48类的元素,建议给Dropdown根元素加唯一id(比如id="dropdown-container"),再用document.querySelector('#dropdown-container')精准选择。
2. 确保元素已挂载后再执行代码
如果在组件未渲染完成时执行控制台代码,dropdown会返回null。可以:
- 打开Dropdown后再执行控制台代码
- 或者用
setTimeout延迟执行,确保DOM已更新:
setTimeout(() => { const dropdown = document.querySelector('.w-48'); const handleClick = (event) => { if (dropdown.contains(event.target)) { console.log('Inside dropdown'); } else { console.log('OUTSIDE Dropdown'); } } document.addEventListener('click', handleClick, true); }, 1000);
3. (可选)在React组件内直接实现判断逻辑
如果不需要控制台测试,推荐用React的useRef实现点击范围判断,更符合React开发范式:
import { useState, useRef, useEffect } from "react"; import { GoChevronDown, GoChevronLeft } from "react-icons/go"; import Panel from "./Panel"; function Dropdown({ options, value, onChange }) { const [isOpen, setIsOpen] = useState(false); const dropdownRef = useRef(null); const handleClick = () => { setIsOpen(!isOpen); }; const handleOptionClick = (option) => { setIsOpen(false); onChange(option); }; useEffect(() => { const handleDocumentClick = (event) => { if (dropdownRef.current && dropdownRef.current.contains(event.target)) { console.log('Inside dropdown'); } else { console.log('OUTSIDE Dropdown'); } }; document.addEventListener('click', handleDocumentClick, true); return () => document.removeEventListener('click', handleDocumentClick, true); }, []); const renderedOptions = options.map((option) => { return ( <div className='hover:g-sky-100 rounded cursor-pointer p-1' onClick={() => handleOptionClick(option)} key={option.value} > {option.label} </div> ); }); return ( <div ref={dropdownRef} className='w-48 relative'> <Panel className='flex justify-between items-center cursor-pointer' onClick={handleClick} > {value?.label || "Select..."} {isOpen ? ( <GoChevronDown className='text-3xl' /> ) : ( <GoChevronLeft className='text-3xl' /> )} </Panel> {isOpen && <Panel className='absolute top-full'>{renderedOptions}</Panel>} </div> ); } export default Dropdown;
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

