单击给定组件外部时,如何从 React.JS 中组件内的元素中删除 class?

How do you remove a class from elements inside a component in React.JS when clicking outside given component?

我正在尝试模拟一种类似于在模式弹出窗口打开时单击叠加层的行为。在 sidenav 组件外部单击时,我想关闭当前处于 flyout 模式的所有元素。

我有一个多层嵌套导航菜单,存储在它自己的组件中,Sidebar。我有以下代码处理 clicks 发生在 Sidebar 组件之外的代码:

class Sidebar extends React.Component {
    ...
    handleClick = (e) => {
      if (this.node.contains(e.target)) {
        return;
      }
  
      console.log('outside');
    };
  
    componentDidMount() {
      window.addEventListener('mousedown', this.handleClick, false);
    }

    componentWillUnmount() {
      window.removeEventListener('mousedown', this.handleClick, false);
    }

    render() {
      return (
          <div
              ref={node => this.node = node}
              className="sidebar"
              data-color={this.props.bgColor}
              data-active-color={this.props.activeColor}
          >
          {renderSideBar()}
          </div>
      );
    }
    ...
}

这部分工作正常 - 但当弹出菜单在单击父菜单选项时显示时,我希望它关闭当前打开的任何弹出菜单。

-|
 |
 - Menu Item 1
  |
  |-option 1 (currently open)
  |-option 2
 - Menu Item 2
  | 
  |-option 1 (closed)
  |-option 2 (closed, clicked to expand - this is when it should close [Menu Item 1/Option 1]

在映射包含菜单结构的数据对象时,使用 <li> 标记生成菜单项。

有没有办法基本上 select 所有具有 class 为 'collapse' / aria-expanded="true" 的已注册对象并将其删除?类似于 jQuery 将 select dom 元素和操作它们的方式。

我知道这不是 React 工作的前提,它只是我想模拟的行为的一个例子。

据我了解,您想从另一个组件修改 DOM 子树。要实现您的目标,您可以使用 ref.

当您想访问 HtmlElement API directly - in my example I use animate(). Please, read the documentation 时,使用 ref 很有帮助,因为它描述了更多 ref 用例。

下面是用户点击<Content />animating<Sidebar/>收缩的简单例子。

const { useRef } = React;

function Main() {
  const sidebar = useRef(null);

  const handleClick = () => {
    sidebar.current.hide();
  };

  return (
    <div className="main">
      <Sidebar ref={sidebar} />
      <Content onClick={handleClick} />
    </div>
  );
}

class Sidebar extends React.Component {
  constructor(props) {
    super(props);
    this.state = { visible: true };
    this.show = this.show.bind(this);
    this.sidebar = React.createRef(null);
  }

  show() {
    if (!this.state.visible) {
      this.sidebar.current.animate(
        { flex: [1, 2], "background-color": ["teal", "red"] },
        300
      );
      this.setState({ visible: true });
    }
  }

  hide() {
    if (this.state.visible) {
      this.sidebar.current.animate(
        { flex: [2, 1], "background-color": ["red", "teal"] },
        300
      );
      this.setState({ visible: false });
    }
  }

  render() {
    return (
      <div
        ref={this.sidebar}
        className={this.state.visible ? "sidebar--visible" : "sidebar"}
        onClick={this.show}
      >
        Sidebar
      </div>
    );
  }
}

function Content({ onClick }) {
  return (
    <div className="content" onClick={onClick}>
      Content
    </div>
  );
}

ReactDOM.render(<Main />, document.getElementById("root"));
.main {
  display: flex;
  height: 100vh;
}

.sidebar {
  flex: 1;
  background-color: teal;
}

.sidebar--visible {
  flex: 2;
  background-color: red;
}

.content {
  flex: 7;
  background-color: beige;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>