如何确保一次只打开/展开 1 个面板?

Posted

技术标签:

【中文标题】如何确保一次只打开/展开 1 个面板?【英文标题】:How can I make sure only 1 panel is open/expanded at a time? 【发布时间】:2021-07-21 08:01:10 【问题描述】:

这是一个可以用按钮扩展的卡片的组件/功能。 <Viewmore> 只不过是一个图像组件。不得不使用它,因为图像是 svg。

import React,  useState, useRef  from "react";
import Rating from "@material-ui/lab/Rating";
import Icon1 from "../test.jpeg";
import Viewmore from "./Viewmore";

function Acc(props) 
  const [value, setValue] = React.useState(2);

  const content = useRef(null);

  const [setActive, setActiveState] = useState("");
  const [setHeight, setHeightState] = useState("160px");
  const [setRotate, setRotateState] = useState("icon");

  function toggleAccordion() 
    setActiveState(setActive === "" ? "active" : "");
    setHeightState(
      setActive === "active" ? "160px" : `$content.current.scrollHeightpx`
    );
    setRotateState(setActive === "active" ? "icon" : "icon rotate");
  

  return (
    <div className="accordian" id=props.id>
      <div className="accordianHeading">
        <div
          className="container"
          ref=content
          style= maxHeight: `$setHeight` 
        >
          <img src=Icon1 className="postimg" />

          <div className="title">
            Nulla adipisicing do ea qui enim id cillum reprehenderit aliquip
            nostrud sint fugiat.
          </div>
          <div className="namedate">asdfasdf | 10th April 2021</div>
          <div classname="content">
            <p>
              Aliquip duis excepteur non ullamco eiusmod cillum cupidatat non
              officia laborum esse tempor est nisi. Aute amet irure ex proident
              commodo reprehenderit cupidatat deserunt dolor eu. Culpa elit
              officia sit id reprehenderit nisi amet tempor reprehenderit fugiat
              sint irure. Laborum pariatur nisi amet minim eiusmod ad occaecat
              ipsum aliquip. Aute dolore culpa ad minim voluptate. Lorem in qui
              pariatur nostrud in velit ad sunt irure mollit commodo. Sit
              voluptate occaecat cupidatat dolor veniam aute amet sit ea
              consequat et voluptate id.
            </p>
          </div>
        </div>
      </div>
      <div className="accordianContent">
        <div className="foot">
          <Rating defaultValue=0 precision=1 />
          <button className="button" onClick=toggleAccordion>
            <Viewmore className=`$setRotate` width=20 fill="777" />
          </button>
        </div>
      </div>
    </div>
  );


export default Acc;

单击按钮时,它会启动 3 个进程。首先是触发活动状态的活动。这会触发一个活动的 css 类并旋转按钮图标。

这是路由到 ReactDOM 的 App.js。

import React,  useState  from "react";
import "./App.css";
import Acc from "./components/Acc";

function App() 
  return (
    <div className="App">
      <Acc />
      <Acc />
      <Acc />
      <Acc />
      <Acc />
      <Acc />
      <Acc />
      <Acc />
    </div>
  );


export default App;

如何确保一次只打开 1 张&lt;Acc /&gt; 卡?

【问题讨论】:

【参考方案1】:

有一些不同的方法可以做到这一点,但这是我的建议。

父 (App) 有一个状态是活动卡。它传递给每个孩子

    它是否处于活动状态 onToggle 回调,让父级知道它点击了。

孩子正在监听 (useEffect) 其状态 (active) 并相应地更新 UI。

App.js

const cards = [1, 2, 3, 4, 5, 6, 7, 8];

export default function App() 
  const [active, setActive] = useState();
  const onToggle = (id) => 
    setActive(id === active ? null : id);
  ;
  return (
    <div className="App">
      cards.map((card) => (
        <Acc
          key=card
          id=card
          onToggle=onToggle
          active=card === active
        />
      ))
    </div>
  );

Acc.js(仅区别)

useEffect(() => 
  setHeightState(active ? `$content.current.scrollHeightpx` : "160px");
  setRotateState(active ? "icon" : "icon rotate");
, [active]);
function toggleAccordion() 
  onToggle(id);
  // setActiveState(setActive === "" ? "active" : "");
  // setHeightState(
  //   setActive === "active" ? "160px" : `$content.current.scrollHeightpx`
  // );
  // setRotateState(setActive === "active" ? "icon" : "icon rotate");

https://codesandbox.io/s/cranky-cherry-5x9xx?file=/src/App.js


您也可以使用 Context,但我相信这是一种更简单的方法。

【讨论】:

这真是太好了!但是,您如何使它适用于未定义数量的卡片......以及内容不同的卡片? rn 它只是一遍又一遍地映射完全相同的卡,但是如果我们必须重复调用一次,我们如何注入内容呢? 是的。这只是你的一个例子:)我已经更新了代码框,所以现在内容是“动态的”(来自cards.json)。显然这不完全是你的情况,但它展示了这个过程。 哦,好吧,有两件事:1) 是否可以将 content 添加到 而不是从cards.json 中获取?对我的用例更有意义。 2)当触发toggle()时,它会相应地改变状态,但是当另一个卡的另一个切换被击中时,前一个只是关闭,但不会改变任何其他状态?例如:我有一个活动状态......所以当卡打开时,它会略微着色。当我关闭同一张卡片时,色调消失了,但是当我点击另一张卡片时,触发切换(),色调和处于“活动”状态的所有内容都不会改变。 1. text 是您要从父级传递的内容。目前它来自cards.json,但您可以通过任何其他方式替换此逻辑(例如在javascript中生成它或从服务器/api获取。2.已经如此。当active时,Acc组件正在“侦听”已更改并更新其他组件的状态(例如height and rotate)。您还想更改哪些其他状态?如果有什么不清楚,让我们chat 是的,我会告诉你

以上是关于如何确保一次只打开/展开 1 个面板?的主要内容,如果未能解决你的问题,请参考以下文章

一次只展开一个表格行 Ant Design React

如何一次打开所有 Bootstrap 手风琴标签

如何实现像 iOS MAIL 应用程序这样的平移手势,以便一次只打开一个单元格?

Celery 任务计划(确保一个任务一次只执行一个)

一次仅显示一个可扩展列表的子项

如何实现一次只为 1 个客户端提供服务的 WebSocket 服务器