繁体   English   中英

在 React 中单击链接关闭 DropDown 的最佳做法是什么?

[英]What is the best practice to close a DropDown on a Link click in React?

我有以下代码: Link to Sandbox

import React, { useState } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
import "./styles.css";

function DropDown({ close }) {
  return (
    <ul>
      <li>
        <Link to="/" onClick={close}>
          Page 1
        </Link>
      </li>
      <li>
        <Link to="/page2" onClick={close}>
          Page 2
        </Link>
      </li>
      <li>
        <p>Dont hide dropdown when clicking me!</p>
      </li>
    </ul>
  );
}

function Header() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <div>
      <button onClick={() => setIsOpen(prev => !prev)}>Toggle DropDown</button>
      {isOpen && <DropDown close={() => setIsOpen(false)} />}
    </div>
  );
}

function Page1() {
  return <h1>Page 1</h1>;
}

function Page2() {
  return <h1>Page 2</h1>;
}

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Header />
        <Switch>
          <Route exact path="/" component={Page1} />
          <Route path="/page2" component={Page2} />
        </Switch>
      </BrowserRouter>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

如果不总是将close回调向下传递到下拉列表中的每个链接,我该如何实现呢?

我试图用一个钩子来实现这个,这个钩子监听mousedown并检查它是否在下拉列表中并点击类型 A 但问题是它在反应重定向到路由之前关闭下拉列表。

我已经用 useClickAway 钩子覆盖了外面的点击,但我还需要一个 useClickInsideOnLink 钩子。

您可以在DropDown组件中创建一个内部组件,并默认使用close function ,还可以通过rest parameter传递其他道具。 像这样的东西:

import React, { useState } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Switch, Link } from "react-router-dom";
import "./styles.css";

function DropDown({ close }) {

  const MyLink = ({...rest}) => {
    return (
      <Link {...rest} onClick={close}>
        Page 1
      </Link>
    )
  }

  return (
    <ul>
      <li>
        <MyLink to="/">
          Page 1
        </MyLink>
      </li>
      <li>
        <MyLink to="/page2">
          Page 2
        </MyLink>
      </li>
      <li>
        <p>Dont hide dropdown when clicking me!</p>
      </li>
    </ul>
  );
}

function Header() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <div>
      <button onClick={() => setIsOpen(prev => !prev)}>Toggle DropDown</button>
      {isOpen && <DropDown close={() => setIsOpen(false)} />}
    </div>
  );
}

function Page1() {
  return <h1>Page 1</h1>;
}

function Page2() {
  return <h1>Page 2</h1>;
}

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Header />
        <Switch>
          <Route exact path="/" component={Page1} />
          <Route path="/page2" component={Page2} />
        </Switch>
      </BrowserRouter>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

我认为您(至少)有两个选择:

  • 将 Header 移动到页面中(由于呈现新页面,header 被重新初始化)。 在此处分叉您的 CodeSandbox
  • 让 DropDown 自己处理它的 state,并将切换按钮包含到下拉列表中。 在此处查看 CodeSandbox

另一种可能的方式(我不推荐它,因为它给一个简单的问题增加了不必要的复杂性)(由提问者的评论添加):

  • 实际上,当 props 更改或 state 更改时,组件会更新。 引用 react 文档:如果您想在 prop 更改时“重置”某些 state ,请考虑使用密钥完全控制完全不受控制的组件。 完全不受控制的将是选项 2。

    要触发道具的变化,您可以将Header与Router 连接。 这样,当位置(和其他路由道具)发生变化时,Header 组件就会得到通知。 基于此,您可以通过生命周期方法组件DidUpdate 对 isOpen state 应用更新,并将先前的 location.pathname 与当前的比较,如果路径名更改,则将 isOpen 设置回 false。 其实我不建议这样做。 但是在这里你 go: CodeSandbox

可能为时已晚,但如果它不能帮助某人:您可以从 useLocation() 挂钩中解构路径名,并且在每次路径名更改时,您可以在 useEffect 中将 isOpen 设置为 false。

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Switch, Link, useLocation } from "react-router-dom";
import "./styles.css";

function DropDown({ close }) {

  const MyLink = ({...rest}) => {
    return (
      <Link {...rest}>
        Page 1
      </Link>
    )
  }

  return (
    <ul>
      <li>
        <MyLink to="/">
          Page 1
        </MyLink>
      </li>
      <li>
        <MyLink to="/page2">
          Page 2
        </MyLink>
      </li>
      <li>
        <p>Dont hide dropdown when clicking me!</p>
      </li>
    </ul>
  );
}

function Header() {
  const [isOpen, setIsOpen] = useState(false);
  
  const { pathname } = useLocation();
  useEffect(() =>{
    setIsOpen(false);
  }, [pathname])

  return (
    <div>
      <button onClick={() => setIsOpen(prev => !prev)}>Toggle DropDown</button>
      {isOpen && <DropDown close={() => setIsOpen(false)} />}
    </div>
  );
}

function Page1() {
  return <h1>Page 1</h1>;
}

function Page2() {
  return <h1>Page 2</h1>;
}

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Header />
        <Switch>
          <Route exact path="/" component={Page1} />
          <Route path="/page2" component={Page2} />
        </Switch>
      </BrowserRouter>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

尝试像这样订阅当前位置:

const location = useLocation();
useEffect(() => {
  closeHeader();
}, [location]);

我猜这个不错;3

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM