Javascript 如何在React钩子中检测多个外部单击事件侦听器时提高性能

Javascript 如何在React钩子中检测多个外部单击事件侦听器时提高性能,javascript,reactjs,performance,react-hooks,Javascript,Reactjs,Performance,React Hooks,在搜索主题“”后,我无法找到提高当前应用程序性能的解决方案 上下文:我有多个组件: 应用程序:根组件具有项目选择状态以检测当前项目选择(第一个组件或第二个组件,两个不同的组件)。它有一个mousedown/mouseup事件侦听器来检测外部单击FirstComponent或SecondComponent SecondComponent:圆圈组件选择了道具,道具经过App。可以选择(内部单击)/取消选择(外部单击或选择后再次单击) 问题:同时,它有一些外部点击事件监听器冗余。 示例:单击F

在搜索主题“”后,我无法找到提高当前应用程序性能的解决方案

  • 上下文:我有多个组件:

    • 应用程序:根组件具有
      项目选择
      状态以检测当前项目选择(
      第一个组件
      第二个组件
      ,两个不同的组件)。它有一个
      mousedown/mouseup
      事件侦听器来检测外部单击
      FirstComponent
      SecondComponent
    • SecondComponent:圆圈组件选择了
      道具,道具经过
      App
      。可以选择(内部单击)/取消选择(外部单击或选择后再次单击)
  • 问题:同时,它有一些外部点击事件监听器冗余。 示例:单击
    FirstComponent
    /
    SecondComponent
    中的一个组件时,会出现:
  • 目标:如何一次检测一次多个组件上的外部单击事件侦听器? 我想优化:当单击
    FirstComponent
    /
    SecondComponent
    中的一个时,只调用外部的单击
    DropdownComponent
  • 注意:事实上,我想在每个组件(
    FirstComponent
    /
    SecondComponent
    )中实现ref
    useRef
    )来解决它,但我不知道如何解决(在App.js中修复我)

    源代码演示已打开


    提前谢谢

    我认为您的问题在于,当您单击所选组件时,会首先取消选择它们。这是因为onClick事件在释放鼠标按钮时触发,而外部单击在鼠标按下时触发。这可以通过在addEventListener中用mouseup替换mousedown以进行外部单击来解决

    另一个潜在的问题可能是,您没有从窗口中删除侦听器。您应该在useEffect挂钩的返回函数中清除所有订阅。为此,最好将事件处理程序声明移动到useEffect中。由于外部单击事件处理程序使用itemSelecting值,因此必须将其作为依赖项添加到useEffect

    useEffect(() => {
      const handleClickOutside = (event) => {
        if (itemSelecting !== -1) setItemSelecting(0);
        console.log('click first/second component');
      }
      document.addEventListener('mouseup', handleClickOutside);
      return () => document.removeEventListener('mouseup', handleClickOutside);
    }, [itemSelecting]);
    
    它是可选的,但我将从handleClickOutside处理程序中删除itemSelecting依赖项。所以useffect只调用一次。这样可以更简单

    useEffect(() => {
      const handleClickOutside = (event) => {
        setItemSelecting(0);
        console.log('click first/second component');
      }
      document.addEventListener('mouseup', handleClickOutside);
      return () => document.removeEventListener('mouseup', handleClickOutside);
    }, []);
    
    我希望有帮助。

    mouseup解决方案非常适合我,非常感谢!
    import React from 'react';
    
    import cn from 'classnames';
    
    import styles from '../styles.module.css';
    
    const SecondComponent = ({ label, selected, handleClick }) => {
      const selectedClassName = selected ? styles.selected : '';
    
      return (
        <div
          className={cn([styles.component, styles.second, selectedClassName])}
          onClick={handleClick}>
          <span>{label}</span>
        </div>
      );
    };
    
    export default SecondComponent;
    
    import React, { useState, useRef, useEffect } from 'react';
    import PropTypes from 'prop-types';
    import styles from '../styles.module.css';
    
    const DropDownComponent = () => {
      const nodeRef = useRef(null);
      const listRef = useRef(null);
    
      const [isOpen, setIsOpen] = useState(false);
    
      const handleClick = event => {
        if (nodeRef && nodeRef.current.contains(event.target)) {
          // inside click
          if (
            listRef &&
            listRef.current &&
            listRef.current.contains(event.target)
          ) {
            setTimeout(() => {
              setIsOpen(false);
            }, 500);
          }
          return;
        }
        setIsOpen(false);
        console.log('outside click dropdown');
      };
    
      useEffect(() => {
        document.addEventListener('mousedown', handleClick);
        return () => document.removeEventListener('mousedown', handleClick);
      }, []);
    
      return (
        <div ref={nodeRef} className={styles.dropdown}>
          <button className={styles.dropbtn} onClick={() => setIsOpen(!isOpen)}>
            Dropdown
          </button>
    
          {isOpen && (
            <div ref={listRef} className={styles.dropdownContent}>
              <a>Link 1</a>
              <a>Link 2</a>
              <a>Link 3</a>
            </div>
          )}
        </div>
      );
    };
    
    DropDownComponent.defaultProps = {
      classNameType: null,
      isOpen: false,
      handleIsOpen: () => {},
      title: null,
    };
    
    DropDownComponent.propTypes = {
      classNameType: PropTypes.string,
      isOpen: PropTypes.bool,
      handleIsOpen: PropTypes.func,
      title: PropTypes.string,
    };
    
    export default DropDownComponent;
    
    outside click dropdown 
    click first/second component
    
    useEffect(() => {
      const handleClickOutside = (event) => {
        if (itemSelecting !== -1) setItemSelecting(0);
        console.log('click first/second component');
      }
      document.addEventListener('mouseup', handleClickOutside);
      return () => document.removeEventListener('mouseup', handleClickOutside);
    }, [itemSelecting]);
    
    useEffect(() => {
      const handleClickOutside = (event) => {
        setItemSelecting(0);
        console.log('click first/second component');
      }
      document.addEventListener('mouseup', handleClickOutside);
      return () => document.removeEventListener('mouseup', handleClickOutside);
    }, []);