Маршрут React-Router создает нулевую ссылку из размонтированного функционального компонента

#reactjs #react-router

#reactjs #react-router

Вопрос:

Кто-то с опытом работы с React-Router, вероятно, может ответить на этот вопрос. У меня есть приложение с целевой страницей, выпадающим меню на этой целевой странице и кнопкой «зарегистрироваться» <Link> , которая ведет на простую страницу с формой входа.

Выпадающее меню использует систему react ref для размещения ссылки в выпадающем меню. Существует прослушиватель событий щелчка, который ссылается на эту ссылку, чтобы состояние выпадающего списка не переключалось при нажатии на что-либо внутри этой ссылки. И для этого прослушивателя событий click есть функция очистки, чтобы при закрытии меню прослушиватель событий больше не присутствовал.

Происходит то, что если открыто выпадающее меню, и вы нажимаете на <Link> страницу входа в систему, оно создает нулевую ссылку в выпадающем меню из прослушивателя событий щелчка, хотя я пытаюсь полностью размонтировать эти компоненты и смонтировать другой с помощью react-router.

Мне неясно, должен ли я преобразовывать этот LandingMenu компонент в class component, чтобы я мог использовать componentDidUnmount для запуска функции очистки, или я неправильно использую react router, или есть другой «правильный» метод для решения этой проблемы, о котором я не знаю. Вот код.

App.js

 import React, { Component } from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";

import "../styles/index.css";

import Landing from "./landing/Landing";
import Login from "./login/Login";

class App extends Component {
  render() {
    return (
      <div>
        <BrowserRouter>
          <Switch>
            <Route path="/" exact component={Landing} />
            <Route path="/login" exact component={Login} />
          </Switch>
        </BrowserRouter>
      </div>
    );
  }
}

export default App;
  

Кнопка входа в систему

 import React from "react";
import { Link } from "react-router-dom";

import Button from "../common/Button";
import ChevronRight from "../icons/ChevronRight";

const LandingAuthentication = () => {
  return (
    <div className="login-buttons-wrapper">
      <Link to="/login">
        <Button Text="Sign in" Style="button-primary" Icon={<ChevronRight />} />
      </Link>
    </div>
  );
};

export default LandingAuthentication;
  

Меню посадки

 import React, { useState, useEffect, useRef } from "react";

import "../../styles/landing-menu.css";

const LandingMenu = ({ Title, Icon, children }) => {
  // visibility toggle, default closed
  const [open, setOpen] = useState(false);

  // set menu ref to prevent double click event
  const menuRef = useRef();

  // close menu if clicked anywhere outside of menu
  // on initial render, add click event listener
  useEffect(() => {
    const onBodyClick = (event) => {

      // check if element clicked is inside of menu
      // if so no action is required from this event listener so exit
      if (menuRef.current.contains(event.target)) {
        return;
      }
      // else close the menu
      setOpen(false);
    };

    // add event listener and cleanup only if menu is open
    if (open === true) {
      document.body.addEventListener("click", onBodyClick);

      // CLEANUP
      // remove event listener
      return () => {
        document.body.removeEventListener("click", onBodyClick);
      };
    }
  }, [open]);

  // on click toggle state
  return (
    <nav className="landing-menu" ref={menuRef}>
      <div
        className="landing-menu-title-wrapper"
        onClick={() => {
          setOpen(!open);
        }}
      >
        <h3 className="landing-menu-title">{Title}</h3>
        <div className="landing-menu-title-icon">{Icon}</div>
      </div>
      <ul className={`dropdown ${open ? "visible" : "hidden"}`}>{children}</ul>
    </nav>
  );
};

export default LandingMenu;
  

Ответ №1:

Ошибка оказалась вызвана обновлением способа обработки ссылок React в React 17. Решение состоит в том, чтобы обновить if инструкцию из:

if (menuRef.current.contains(event.target)) {

Для

if (menuRef.current amp;amp; menuRef.current.contains(event.target)) {