DEV Community

EVO-9
EVO-9

Posted on

Using a hook inside .map

Hi

Firstly, apologies if my terms are not correct.

I am learning React and I have created a simple accordion component. This works as it should but I have found an issue trying to make it reusable with arrays that are structured differently.

I have managed to use a hook which allows me to either use my blogs.json file or my stories.json file like so:

<Accordion array={stories}/>
<Accordion array={blogs}/>
Enter fullscreen mode Exit fullscreen mode

Now my issue is, my Accordion component uses .map to gather the information, I have asked it to look for i.e. item.content but the blogs array doesn't have content, the blogs array uses body.

I tried using the following:

<Accordion array={stories} content={stories.content}/>
<Accordion array={blogs} content={stories.body}/>
Enter fullscreen mode Exit fullscreen mode

but that won't work. I was thinking I need to change the following line in the component

<p>{item.content}</p>
Enter fullscreen mode Exit fullscreen mode

to something like:

<p>{content}</p>
Enter fullscreen mode Exit fullscreen mode

Which I can pass some static text and it work fine but nothing from the array.

Here's my component,

import { useState, useEffect, useRef } from "react";
import styled from "styled-components";
import stories from "../data/Stories.json";

const Accordion = ({ array }) => {
  const [activeIndex, setActiveIndex] = useState(null);
  const ref = useRef();

  useEffect(() => {
    const onBodyClick = (event) => {
      if (ref.current && ref.current.contains(event.target)) {
        return;
      }
    };
    document.body.addEventListener("click", onBodyClick);
    return () => {
      document.body.removeEventListener("click", onBodyClick);
    };
  }, []);

  const handleChange = (index) => {
    setActiveIndex(activeIndex === index ? null : index);
  };

  const renderedItems = array.map((item, index, key) => {
    return (
      <div key={item.title}>
        <div
          ref={ref}
          className={`${activeIndex === index ? "active" : ""} title`}
          onClick={() => handleChange(index)}
        >
          <i className="dropdown icon"></i>
          {item.title}
        </div>
        <div className={`${activeIndex === index ? "active" : ""} content`}>
          <p>{item.content}</p>
        </div>
      </div>
    );
  });
  return <div className="ui styled accordion">{renderedItems}</div>;
};

export default Accordion;

Enter fullscreen mode Exit fullscreen mode

How do I overcome this?

Top comments (2)

Collapse
 
vonheikemen profile image
Heiker

I believe you're on the right track. Try changing this.

<p>{item.content}</p>
Enter fullscreen mode Exit fullscreen mode

To this.

<p>{item[content]}</p>
Enter fullscreen mode Exit fullscreen mode

Don't forget to use content inside the component.

const Accordion = ({ array, content }) =>
Enter fullscreen mode Exit fullscreen mode

And now in the accordion component pass a static text with the property name.

<Accordion array={blogs} content={'body'}/>
Enter fullscreen mode Exit fullscreen mode

If you want to get fancy you can turn the prop content into a function.

<p>{content(item)}</p>
Enter fullscreen mode Exit fullscreen mode

You could do this.

<Accordion array={blogs} content={(item) => item.body}/>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
evo9 profile image
EVO-9

Thank you. That worked perfectly. I thought I might be close but I just couldn't get over the hurdle. Thanks