React检测元素是否超出窗口:useOnScreen钩子优化需求
问题与解决方案
问题描述
我正在编写代码检测div元素是否超出窗口范围,当前代码存在两个问题:
- 当
isOpen状态更新时,会忽略检测结果 - 无法识别元素轻微超出窗口的情况
原代码:
export const useOnScreen = (ref, rootMargin = '0px') => { const [isIntersecting, setIntersecting] = useState(false); useEffect(() => { const observer = new IntersectionObserver( ([entry]) => { setIntersecting(entry.isIntersecting); }, { rootMargin } ); if (ref.current) { observer.observe(ref.current); } return () => { observer.unobserve(ref.current); }; }, []); return isIntersecting; }; export default useOnScreen;
原使用示例:
const packRef = useRef(null); const isVisible = useOnScreen(packRef); { !optionsOpen && ( <div className="" ref={ packRef }>
原Tailwind类名使用:
<div className={ classNames('absolute top-full right-0 w-full bg-white mt-2 rounded-lg overflow-hidden shadow-2xl', { '!bottom-full': !isVisible }) } ref={ observe } >

问题分析
- 依赖缺失:原
useEffect依赖数组为空,当isOpen切换导致元素挂载/卸载、或ref指向变化时,不会重新创建观察者,导致检测失效 - 检测阈值不敏感:默认
rootMargin: '0px'和默认threshold: 1.0(IntersectionObserver默认阈值)要求元素完全在视口内才判定为可见,轻微超出时仍会被判定为相交
修改后的代码
import { useState, useEffect, useRef } from 'react'; export const useOnScreen = (ref, rootMargin = '-1px', threshold = 0.01) => { const [isIntersecting, setIntersecting] = useState(false); const observerRef = useRef(null); useEffect(() => { // 清理旧观察者 if (observerRef.current) { observerRef.current.disconnect(); } // 创建新观察者,调整检测规则 const observer = new IntersectionObserver( ([entry]) => { setIntersecting(entry.isIntersecting); }, { root: null, // 以视口为检测根容器 rootMargin, // 缩小检测范围,轻微超出即判定为不可见 threshold // 只要元素极小部分在视口内就判定为可见 } ); observerRef.current = observer; const currentEl = ref.current; if (currentEl) { observer.observe(currentEl); } return () => { if (currentEl) observer.unobserve(currentEl); observer.disconnect(); }; }, [ref, rootMargin, threshold]); // 添加依赖,确保状态变化时重新绑定检测 return isIntersecting; }; export default useOnScreen;
调整后的使用方式
基础使用
const packRef = useRef(null); const [optionsOpen, setOptionsOpen] = useState(false); // 可根据需求自定义检测严格程度,比如更严格的检测:rootMargin: '-5px', threshold: 0.1 const isVisible = useOnScreen(packRef, '-1px', 0.01); {!optionsOpen && ( <div className="" ref={packRef}> {/* 你的元素内容 */} </div> )}
Tailwind类名适配
<div className={classNames( 'absolute top-full right-0 w-full bg-white mt-2 rounded-lg overflow-hidden shadow-2xl', { '!bottom-full': !isVisible // 元素超出视口时切换定位逻辑 } )} ref={packRef} > {/* 你的元素内容 */} </div>
内容的提问来源于stack exchange,提问作者Aleksandrs
相关产品推荐
相关产品推荐

