React:如何打开新添加的手风琴项目?

React: How to open a newly added accordion item?

我的 React 应用程序中有一个手风琴组件。用户可以向此手风琴中添加项目。现在,当用户单击按钮向手风琴添加项目时,我希望新添加的手风琴项目自动打开。这里有一个codesandbox displaying my problem.

要打开和关闭手风琴,我使用了一些状态:const [clicked, setClicked] = useState("0");

单击 accordionItem 时,将调用以下函数:

  const handleToggle = (index) => {
    if (clicked === index) {
      return setClicked("0");
    }
    setClicked(index);
  };

在我的 codesandbox 演示中,我使用一系列问题和答案作为 accordionItems 并循环遍历它们。所以我认为我所要做的就是使用 setClicked 函数将 clicked 状态设置为等于 faqs 数组的长度减 1 以打开正确的手风琴,但事实并非如此工作如预期...

参考代码

Accordion.jsx:

import AccordionItem from "./AccordionItem";
import { useState } from "react";

export default function Accordion() {
  const initialFaqs = [
    {
      question: "Lorem ipsum dolor sit amet?",
      answer:
        "Tenetur ullam rerum ad iusto possimus sequi mollitia dolore sunt quam praesentium. Tenetur ullam rerum ad iusto possimus sequi mollitia dolore sunt quam praesentium.Tenetur ullam rerum ad iusto possimus sequi mollitia dolore sunt quam praesentium."
    },
    {
      question: "Dignissimos sequi architecto?",
      answer:
        "Aperiam ab atque incidunt dolores ullam est, earum ipsa recusandae velit cumque. Aperiam ab atque incidunt dolores ullam est, earum ipsa recusandae velit cumque."
    },
    {
      question: "Voluptas praesentium facere?",
      answer:
        "Blanditiis aliquid adipisci quisquam reiciendis voluptates itaque."
    }
  ];

  const [faqs, setFaqs] = useState(initialFaqs);

  const [clicked, setClicked] = useState("0");

  const handleToggle = (index) => {
    if (clicked === index) {
      return setClicked("0");
    }
    setClicked(index);
  };

  const addFaq = () => {
    let newFaq = [...faqs];
    newFaq.push({
      question: "This accordion item should automatically open",
      answer:
        "Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur."
    });
    setFaqs(newFaq);

    //I want to open the accordion of the item that is just openend.
    //Currently, setting the clicked state equal to the length of the faqs gives an error.
    //Setting clicked equal to faqs.length - 1 does not open the newly added faq...
    //How to solve?
    setClicked(faqs.length);
  };

  return (
    <div>
      <ul className="accordion">
        {faqs.map((faq, index) => (
          <AccordionItem
            onToggle={() => handleToggle(index)}
            active={clicked === index}
            key={index}
            faq={faq}
          />
        ))}
      </ul>
      <button onClick={() => addFaq()} className="add-faq-btn">
        Add question
      </button>
    </div>
  );
}

AccordionItem.jsx:

import { useRef } from "react";

export default function AccordionItem({ faq, active, onToggle }) {
  const contentEl = useRef();

  const { question, answer } = faq;
  return (
    <li className={`accordion_item ${active ? "active" : ""}`}>
      <button className="button" onClick={onToggle}>
        {question}
        <span className="control">{active ? "—" : "+"} </span>
      </button>
      <div
        ref={contentEl}
        className="answer_wrapper"
        style={
          active
            ? { height: contentEl.current.scrollHeight }
            : { height: "0px" }
        }
      >
        <div className="answer">{answer}</div>
      </div>
    </li>
  );
}

你的问题不大。

一个是你必须在添加过程中使用下面的newFaq(因为状态变量不会立即更新):

setClicked(newFaq.length - 1);

修复后你会得到另一个错误。那是因为您试图在 style 对象中渲染期间访问 DOM 元素的 ref:由于组件尚未放置在 DOM 上,因此 ref 为空。您将需要这样的东西:

  React.useEffect(() => {
    setHeight(contentEl.current.scrollHeight);
  }, []);

注意:老实说,我不确定上面的 useEffect 哪个是正确的依赖项,如果我们这样使用它,它将始终使用初始高度。如果某些项目的高度之后没有改变,这可能没问题。

并在样式

中使用height
style={active ? { height } : { height: "0px" }}