1

I am trying to build a reusable accordion, i was able to create an accordion with one level, but here i am stuck to have the nested accordion.

What i have tried so far

App.js

import "./styles.css";
import Accordion from "./Accordion";
import LIST from './Constants';


const listMaker = (item) => {
  let faqItem;
  if (item.children.length === 0) {
    faqItem = (
      <>
        <Accordion title={item.name}></Accordion> <hr />
      </>
    );
  } else {
    let faqItemChildren = item.children.map((item) => {
      let faqItem = listMaker(item);
      return (
        <>
          ${faqItem}
          <hr />
        </>
      );
    });
    faqItem = <Accordion title={item.name}>{faqItemChildren}</Accordion>;
  }
  return faqItem;
};

let listItems = LIST.map((item) => {
  let menuItem = listMaker(item);
  return menuItem;
});

export default function App() {
  return listItems;
}

have added codesandbox

I am new tor react, Any help is appreciated

dev
  • 814
  • 10
  • 27

2 Answers2

2

Instead of using dangerouslySetInnerHTML you can use the children, as you need is a spread of React.ReactChildren. That would be just calling the {children} from props instead of the dangerouslySetInnerHTML

    <div className="accordion__section">
      <button className={`accordion ${setActive}`} onClick={toggleAccordion}>
        <p className="accordion__title">{title}</p>
        <Chevron className={`${setRotate}`} width={10} fill={"#777"} />
      </button>
      <div
        ref={content}
        style={{ maxHeight: `${setHeight}` }}
        className="accordion__content"
      >
        {children}
      </div>
    </div>

Here is a forked solution of your codesandbox.

Also, Instead of setting the DOM to a variable, as its a conditional scenario, you can use the ternary operator, which helps in better readability.

const listMaker = (item) => {
  return (
    <>
      {item.children.length === 0 ? (
        <Accordion title={item.name} />
      ) : (
        <Accordion title={item.name}>
          {item.children.map((childItem) => {
            return listMaker(childItem);
          })}
        </Accordion>
      )}
    </>
  );
};
Shujath
  • 846
  • 1
  • 8
  • 22
  • Many Thanks, just one doubt, how to make my nested accordion close when i close my parent accordion, while checking the forked codesandbox had this doubt, is there a way ? – dev May 03 '21 at 09:10
  • any way for doing the above – dev May 03 '21 at 11:19
  • For that, I think you can do two things, one is can push the active state up to the parent i.e container of all accordions, and handle it from there, using the data retrieved from the LIST json, or the other way is to keep a single accordion open at one point of time that would pushing up the onChange of accordion to the parent, and just setting the active state. – Shujath May 03 '21 at 15:26
  • Is there any reference for this, may be i am new bie i didn't understood completely – dev May 03 '21 at 16:34
  • 1
    Here is an approach for your problem. I also changed some bad practices, so you can observe and get a better idea. [codesandbox](https://codesandbox.io/s/awesome-wozniak-zr3m3) – Remzi Atay May 03 '21 at 18:38
  • When i try to use nested i am getting an height could you help me on this https://stackoverflow.com/questions/67396013/controlling-the-height-inside-a-section-for-nested-accordion/67396570?noredirect=1#comment119126199_67396570 – dev May 05 '21 at 07:17
  • @RemziAtay one doubt on this https://codesandbox.io/s/awesome-wozniak-zr3m3 if i need to add a text inside test 1a how could i do that assume if i add content prop in { id: "1a", name: "Test 1 A", children: [], content : "Test 1 a Data will here" }, where should do the changes the accordion.js – dev May 05 '21 at 07:50
  • You can put `{props.item.content}` wherever you want to render it (eg. in accordion__text div) and it should work – Remzi Atay May 05 '21 at 10:56
  • Hi @RemziAtay I had one doubt regarding on this question https://stackoverflow.com/questions/70063149/event-timeline-with-animation If you can help me on this it will be really helpful, many thanks – dev Nov 23 '21 at 10:59
1

dangerouslySetInnerHTML is to use with strings. You shouldn't give an array of components to it. Yet you don't send any prop called content anyway. I think you meant children prop there. Just render children instead of using dangerouslySetInnerHTML

In your Accordion component replace this:

<div
  className="accordion__text"
  dangerouslySetInnerHTML={{ __html: props.content }}
/>

With this:

<div className="accordion__text"> 
  { props.children }
</div>
Remzi Atay
  • 131
  • 2
  • 9
  • Many Thanks i have missed – dev May 03 '21 at 09:14
  • i have revisited the code, i had a doubt. https://codesandbox.io/s/ecstatic-monad-1m068. check this if i set auto then the content inside scroll will go but smooth scrolling transition won't work. Is there a way where i can avoid scroll inside of content and achieve smooth transition on open and close of accordion – dev Jun 04 '21 at 13:27
  • @ Remzi any way to achieve this, i have tried but open will work but the close won't – dev Jun 05 '21 at 17:08