skip to Main Content

Wondering how could I display a separator at any position in JSX? For instance, I have the following list which displays text when clicking on the item:

enter image description here

The code is the following:

<Accordion>
  {items.map((item, index) => (
    <AccordionItem key={item.id} item={item} index={index} />
  ))}
</Accordion>

Having an object like this declaring the content of the list:

const items = [
    { label: "One", content: "lorem ipsum for more, see https://one.com" },
    { label: "Two", content: "lorem ipsum for more, see https://two.com" },
    {
      label: "Three",
      content: "lorem ipsum for more, see https://three.com",
    },
  ];

Now, I’d like to add a separator after the label "Two" to get something like this:
enter image description here

If we try the following way, I get active the element in index 0 and element in index2:

<Accordion>
  {items.slice(0, 2).map((item, index) => (
    <AccordionItem key={item.id} item={item} index={index} />
  ))}
  <hr />
  {items.slice(2).map((item, index) => (
    <AccordionItem key={item.id} item={item} index={index} />
  ))}
</Accordion>

Then, getting this incorrect result:
enter image description here

Why it is happening this? Because my Accordion object is the following:

import { useState, createContext } from "react";

export const AccordionContext = createContext({
  activeItemIndex: 0,
  setActiveItemIndex: () => 0,
});

export const Accordion = (props) => {
  const [activeItemIndex, setActiveItemIndex] = useState(0);

  return (
    <AccordionContext.Provider value={{ activeItemIndex, setActiveItemIndex }}>
      <ul>{props.children}</ul>
    </AccordionContext.Provider>
  );
};

At the beginning activeItemIndex is 0, so when applying slice(2) the first element is also index 0 (however the element is at index 2).

How could I get the line right after the second element in the list?

Thanks a lot

2

Answers


  1. You can use the index of the array to display the separator conditionally.

    
     <Accordion>
       {items.map((item, index) => (
          <React.Fragment key={item.id}>
            <AccordionItem item={item} index={index} />
            {index === 1 && <hr />}
          </React.Fragment>
        ))}
      </Accordion>
    
    
    Login or Signup to reply.
  2. You can conditionally render the separator based on the current index.

    ...
    return (
        <Accordion>
          {items.map((item, index) => (
            <React.Fragment key={item.id}>
              <AccordionItem item={item} index={index} />
              {index === 1 && <hr />}
            </React.Fragment>
          ))}
        </Accordion>
      );
    

    Demo here


    Dynamic Solution for any index value:

    So if you see the above solution works for specific index i.e. 1(means the second <AccordionItem />). To make the separator position dynamic, you can pass a prop to the Accordion component specifying the separator indices which you can then leverage to conditionally render the separator.

    export const Accordion = ({ separators, children }) => {
      const [activeItemIndex, setActiveItemIndex] = useState(0);
    
      return (
        <AccordionContext.Provider value={{ activeItemIndex, setActiveItemIndex }}>
          <ul>{children(separators)}</ul>
        </AccordionContext.Provider>
      );
    };
    // To use
     // const separatorIndices = [2]; // or any dynamic index which you want to provide.
      const separatorIndex = 1; // or this whichever suits your needs
    
      return (
        <Accordion>
          {items.map((item, index) => (
            <React.Fragment key={item.id}>
              <AccordionItem item={item} index={index} />
              {/* {separatorIndices.includes(index) && <hr />} */}
              {index === separatorIndex && <hr />}
            </React.Fragment>
          ))}
        </Accordion>
      );
    

    Demo here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search