Как удалить класс из элементов внутри компонента в React. JS при нажатии вне данного компонента? - PullRequest
0 голосов
/ 06 августа 2020

Я пытаюсь имитировать поведение, подобное нажатию на оверлей, когда открыто модальное всплывающее окно. При нажатии за пределами компонента 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> при сопоставлении объекта данных, содержащего структуру меню.

Есть ли способ в основном выбрать все зарегистрированные объекты, которые имеют класс 'collapse' / aria-extended = "true", и удалить его? Подобно тому, как jQuery будет выбирать элементы dom и манипулировать ими.

Я знаю, что это не предпосылка, в которой работает React, это просто пример поведения, которое я хочу имитировать.

1 Ответ

1 голос
/ 07 августа 2020

Насколько я понимаю, вы хотите изменить поддерево DOM из другого компонента. Чтобы достичь своей цели, вы можете использовать ref.

Использование ref полезно, когда вы хотите получить доступ к HtmlElement API напрямую - в моем примере я использую animate(). Пожалуйста, прочтите документацию , так как в ней описаны другие ref варианты использования.

Ниже приведен простой пример анимации <Sidebar/> сжатия, когда пользователь нажимает <Content />.

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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...