Развернуть / свернуть все данные

#javascript #css #reactjs #accordion #collapse

#javascript #css #reactjs #аккордеон

Вопрос:

Я делаю аккордеон, и когда мы нажимаем на каждый отдельный элемент, он хорошо открывается или закрывается.

Теперь я реализовал опцию развернуть все или свернуть все, чтобы заставить все аккордеоны расширяться / сворачиваться.

Accordion.js

   const accordionArray = [
    { heading: "Heading 1", text: "Text for Heading 1" },
    { heading: "Heading 2", text: "Text for Heading 2" },
    { heading: "Heading 3", text: "Text for Heading 3" }
  ];
  .

  .

  .
{accordionArray.map((item, index) => (
        <div key={index}>
          <Accordion>
            <Heading>
              <div className="heading-box">
                <h1 className="heading">{item.heading}</h1>
              </div>
            </Heading>
            <Text expandAll={expandAll}>
              <p className="text">{item.text}</p>
            </Text>
          </Accordion>
        </div>
      ))}
  

И text.js это файл, в котором я выполняю действие для открытия любого конкретного содержимого аккордеона и кода следующим образом,

 import React from "react";

class Text extends React.Component {
  render() {
    return (
      <div style={{ ...this.props.style }}>
        {this.props.expandAll ? (
          <div className={`content open`}>
            {this.props.render amp;amp; this.props.render(this.props.text)}
          </div>
        ) : (
          <div className={`content ${this.props.text ? "open" : ""}`}>
            {this.props.text ? this.props.children : ""}
            {this.props.text
              ? this.props.render amp;amp; this.props.render(this.props.text)
              : ""}
          </div>
        )}
      </div>
    );
  }
}

export default Text;
  

Здесь через this.props.expandAll я получаю значение, является ли значение расширения true или false. Если это верно, то все accordion получат класс className={`content open`} , поэтому все будут открыты.

Проблема:

open Класс применяется, но внутреннее текстовое содержимое не отображается.

Итак, эта строка не работает,

 {this.props.render amp;amp; this.props.render(this.props.text)}
  

Требование:

Если нажать кнопку развернуть все / свернуть все, то все аккордеоны должны открываться / закрываться соответственно.

Это должно работать независимо от ранее открытого / закрытого аккордеона.. Итак, если развернуть все, то он должен открыть все аккордеоны, иначе необходимо закрыть все аккордеоны, даже если они были открыты / закрыты ранее.

Ссылки:

Это ссылка на файл https://codesandbox.io/s/react-accordion-forked-sm5fw?file=/src/GetAccordion.js то, где на самом деле находится реквизит, передается по наследству.

Редактировать реакцию-аккордеон (разветвленный)

Редактировать:

Если я использую {this.props.children} , то открывается каждый аккордеон.. Никаких проблем.

Но если я открою любой аккордеон вручную, щелкнув по определенному элементу, тогда, если я нажму «Развернуть все», он будет расширен (ожидается), но если я нажму «Свернуть все», то не все аккордеоны будут закрыты.. Те, которые мы открыли ранее, все еще находятся в открытом состоянии.. Но ожидаемое поведение здесь заключается в том, что все должно быть закрыто.

Ответ №1:

В вашем файле text.js

в строке номер 9. пожалуйста, замените предыдущий код на: {this.props.children}

Попробовал в изолированной среде и сработал для меня.

/// не могу добавить комментарий, поэтому редактирую сам ответ. Accordian.js содержит ваш hook ExpandAll, и заголовок boolean уже выполняется GetAccordian.js . Я предлагаю переместить развернуть все в GetAccordian.js так что вы можете контролировать оба значения.

Комментарии:

1. Спасибо за ваш ответ.. Но здесь, если я нажму заголовок 2 и нажму развернуть все, тогда все откроется, так что все в порядке.. Но если я сделаю collapse, все равно откроется заголовок 2, тогда как он должен свернуть все аккордеоны..

2. Также, если я вручную открою все аккордеоны и нажму развернуть все, ничего не произойдет (ожидаемо). Но если я снова нажму свернуть все, тогда аккордеон не закроется, все по-прежнему открывается.. Итак, не могли бы вы, пожалуйста, помочь мне исправить ручное открытие и свертывание всего, если все содержимое должно свернуться?

Ответ №2:

в этом случае this.props.render не является функцией, а this.props.text не определен, попробуйте заменить эту строку

   <div className={`content open`}>
    {this.props.render amp;amp; this.props.render(this.props.text)}
  </div>
  

этим:

   <div className={`content open`}>
   {this.props.children}
  </div>
  

Редактировать: //
Другим решением является передача свойства ExpandAll компоненту Accordion

   <Accordion expandAll={expandAll}>
    <Heading>
      <div className="heading-box">
        <h1 className="heading">{item.heading}</h1>
      </div>
    </Heading>
    <Text>
      <p className="text">{item.text}</p>
    </Text>
  </Accordion>
  

затем в getAccordion.js

 onShow = (i) => {
      this.setState({
        active: this.props.expandAll ? -1: i,
        reserve: this.props.expandAll ? -1: i
      });

      if (this.state.reserve === i) {
        this.setState({
          active: -1,
          reserve: -1
        });
      }
    };

    render() {
      const children = React.Children.map(this.props.children, (child, i) => {
        return React.cloneElement(child, {
          heading: this.props.expandAll || this.state.active === i,
          text: this.props.expandAll || this.state.active   stage === i,
          onShow: () => this.onShow(i)
        });
      });
      return <div className="accordion">{children}</div>;
    }
  };
  

Комментарии:

1. Спасибо за ваш ответ.. Но здесь, если я нажму заголовок 2 и нажму развернуть все, тогда все откроется, так что все в порядке.. Но если я сделаю collapse, все равно откроется заголовок 2, тогда как он должен свернуть все аккордеоны..

2. Также, если я вручную открою все аккордеоны и нажму развернуть все, ничего не произойдет (ожидаемо). Но если я снова нажму свернуть все, тогда аккордеон не закроется, все по-прежнему открывается.. Итак, не могли бы вы, пожалуйста, помочь мне исправить ручное открытие и свертывание всего, если все содержимое должно свернуться?

3. Еще раз спасибо, и я уже добился этого с помощью componentDidUpdate mentod в getAccordion.js как здесь codesandbox.io/s/react-accordion-forked-x7dsc передав реквизит Аккордеону.

4. Но я реализовал ваше решение здесь codesandbox.io/s/react-accordion-forked-74ui7 и есть также проблема в том, что если мы развернем весь аккордеон, то его невозможно закрыть вручную при нажатии на какой-либо конкретный аккордеон.

5. Да, ваше решение выглядит лучше 😉

Ответ №3:

Основываясь на ответе @lissettdm, мне непонятно, почему getAccordion и accordion являются двумя отдельными объектами. У вас может быть очень веская причина для разделения, но тот факт, что состояния двух компонентов являются взаимозависимыми, указывает на то, что их лучше реализовать как один компонент.

Accordion теперь напрямую управляет состоянием своих дочерних элементов, как и раньше, но без использования getAccordion . Переключение ExpandAll теперь также сбрасывает состояния отдельных элементов.

 const NormalAccordion = () => {
  const accordionArray = [ //... your data ];

  const [state, setState] = useState({
    expandAll: false,
    ...accordionArray.map(item => false),
  });

  const handleExpandAll = () => {
    setState((prevState) => ({
      expandAll: !prevState.expandAll,
      ...accordionArray.map(item => !prevState.expandAll),
    }));
  };

  const handleTextExpand = (id) => {
    setState((prevState) => ({
      ...prevState,
      [id]: !prevState[id]
    }));
  };

  return (
    <>
      <div className="w-full text-right">
        <button onClick={handleExpandAll}>
          {state.expandAll ? `Collapse All` : `Expand All`}
        </button>
      </div>
      <br />
      {accordionArray.map((item, index) => (
        <div key={index}>
          <div className="accordion">
            <Heading handleTextExpand={handleTextExpand} id={index}>
              <div className="heading-box">
                <h1 className="heading">{item.heading}</h1>
              </div>
            </Heading>
            <Text shouldExpand={state[index]}>
              <p className="text">{item.text}</p>
            </Text>
          </div>
        </div>
      ))}
    </>
  );
};
  

Заголовок передает обратно индекс, чтобы родительский компонент знал, какой элемент отключить.

 class Heading extends React.Component {
  handleExpand = () => {
    this.props.handleTextExpand(this.props.id);
  };


  render() {
    return (
      <div
        style={ //... your styles}
        onClick={this.handleExpand}
      >
        {this.props.children}
      </div>
    );
  }
}
  

Текст заботится только об одном реквизите, чтобы определить, должен ли он отображать развернутое содержимое.

 class Text extends React.Component {
  render() {
    return (
      <div style={{ ...this.props.style }}>
        <div
          className={`content ${this.props.shouldExpand ? "open" : ""}`}
        >
          {this.props.shouldExpand ? this.props.children : ""}
        </div>
      </div>
    );
  }
}