dev
dev

Reputation: 916

Dynamic nested accordion with nested children

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

Upvotes: 1

Views: 975

Answers (2)

Shujath
Shujath

Reputation: 1161

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>
      )}
    </>
  );
};

Upvotes: 2

Remzi Atay
Remzi Atay

Reputation: 151

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>

Upvotes: 1

Related Questions