hexaquark
hexaquark

Reputation: 941

React do I need a hook for each component?

Problem Description

I have faced this issue multiple times and am not sure what is the best approach. Say I have 3+ components how can I have a state tracker for each of them? Do I need n useState() hooks for n components?

In this example, I want to change the image displayed with onMouseOver(). The way I did it, the hook state is applied to all the images, which is not what I desire to do.

Restrictions

I want to have eventually a custom drop-down menu with onClick() particular to the image folder. So in that sense, I need an individual state tracker for each component?

Code and Snippet

const openFolder = "https://i.postimg.cc/V6L7kBCV/folder-Open.png";
const closedFolder = "https://i.postimg.cc/hG0yn3fm/folder-Closed.png"

function App() {
    const [image, setImage] = React.useState(closedFolder);

    const handleMouseOver = () => {
        setImage(openFolder);
     }
     
     const handleMouseLeave = () =>  {
        setImage(closedFolder);
     }
     
     return (
      <div className="window">
            <div className="slider">
                <div className="container">
                    {buildLatexFolder(image,handleMouseOver,handleMouseLeave)}
                </div>
            </div>
        </div>
    );
   }
 
const buildLatexFolder = (image,handleMouseOver,handleMouseLeave) => {
    const courseNames = [
        'A', 'B', 'C', 'D', 'E', 'F'
        //and many more other
    ]

    var folders = courseNames.map((value, index) => {
        return (
            <div className="folderContainer">
                <span>{value}</span>
                <div onMouseEnter={() => {handleMouseOver()}} onMouseLeave={() => {handleMouseLeave()}}>
                    <img src={image} width="130"/>
                </div>
            </div>
        );
    });
    return folders;
}

   
ReactDOM.render(<App />, document.querySelector("#app"));
.window {
  width: 500px;
  height: 130px;
  display: flex;
  align-items: center;
  margin-left: auto;
  margin-right: auto;
}

.slider {

  border-radius: 0px 0px 16px 16px;
  background-color: #1F1F1F;
  color: #ffffff;

  overflow: hidden;
  float: right;
}

.container {
  display: flex;
  width: 700px;
  height: 130px;
  margin-top: 10px;
  align-items: center;
  text-align: center;
}

.folderElement {
  margin-left: 12px;
  margin-right: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="app"></div>

with JSFiddle here

What I Have Tried

Previously, when faced with this problem I would just make, say, 3 hooks for my 3 components and handle the logic this way. But now I have a large number of components and don't know what is the best approach.

I have searched online 'React Should I have a hook for each component', 'How many hooks to have for components', and so on and so forth but the search results are inaccurate with what I am trying to find, or for that matter my question is inaccurate. I don't know how to proceed.

Does anyone know how I can fix this issue?

Upvotes: 0

Views: 92

Answers (1)

Kavindu Vindika
Kavindu Vindika

Reputation: 2737

You need to have component modularization in your application. Then you can have a single CourseFolder which can be used as a child component resides in your parent component which is App. Using map, you can view multiple child components having different courseDetails inside them.

Refer the following code-snippets I created to solve your issue.

App.js

import React from "react";
import CourseFolder from "./CourseFolder";
// import "./folderStyles.css"; /* kept the styles posted in the question */

const courseNames = [
  "A",
  "B",
  "C",
  "D",
  "E",
  "F",
  //and many more other
];

function App() {
  return (
    <div className="window">
      <div className="slider">
        <div className="container">
          {courseNames.map((value, index) => (
            <CourseFolder value={value} key={index} />
          ))}
        </div>
      </div>
    </div>
  );
}

export default App;

Then create the child component as follows.

CourseFolder.js

import React from "react";

const openFolder = "https://i.postimg.cc/V6L7kBCV/folder-Open.png";
const closedFolder = "https://i.postimg.cc/hG0yn3fm/folder-Closed.png";

function CourseFolder(props) {
  const { value, key } = props;

  const [image, setImage] = React.useState(closedFolder);

  const handleMouseOver = () => {
    setImage(openFolder);
  };

  const handleMouseLeave = () => {
    setImage(closedFolder);
  };

  return (
    <div className="folderContainer" key={key}>
      <span>{value}</span>
      <div
        onMouseEnter={() => {
          handleMouseOver();
        }}
        onMouseLeave={() => {
          handleMouseLeave();
        }}
      >
        <img src={image} width="130" alt="alternative" />
      </div>
    </div>
  );
}

export default CourseFolder;

Hope this would be helpful to solve your issue.

Upvotes: 1

Related Questions