Reputation: 625
I am trying to render an array using the map()
function while giving each element its own unique className (based on the index value) using some established states. An element should change colour when clicked using hooks. I've run into a problem where className = {"header" + {index}.index}
gives the correct state name (header0, header1, etc.) but corresponds to a string rather than the class names established with the same names.
const data = ["James", "John", "Jessica", "Jamie"];
export default function App() {
const [header0, setHeader0] = useState("visable");
const [header1, setHeader1] = useState("visable");
const [header2, setHeader2] = useState("visable");
const [header3, setHeader3] = useState("visable");
const clicked = (index) => {
if (index === 0) {
setHeader0("invisible");
} else if (index === 1) {
setHeader1("invisible");
}
/*Is there an alternative like {setHeader + index} instead of this loop?*/
};
return (
<div className="App">
<h1>Sample Project</h1>
{data.map((value, index) => (
<h1
className={"header" + { index }.index}
onClick={() => {
clicked(index);
}}
>
{/* classname should be the state "header0" but right now its just a string */}
Hello {value}!
</h1>
))}
</div>
);
}
Here is a code sandbox of what I am trying, with a few comments where things are going wrong. Am I going about this problem the correct way? https://codesandbox.io/s/wispy-star-38qvw?fontsize=14&hidenavigation=1&theme=dark
Any help is greatly appreciated!
Upvotes: 3
Views: 4030
Reputation: 168843
You could use a single state array for visibilities.
In addition, if you stash the index in the clicked element's HTML dataset (data-index
), you don't need a separate closure/function for each index.
const data = ["James", "John", "Jessica", "Jamie"];
function App() {
const [visibilities, setVisibilities] = React.useState(() => data.map((x) => true));
const handleClick = (event) => {
const index = parseInt(event.currentTarget.dataset.index, 10);
const newVisibilities = [...visibilities];
newVisibilities[index] = !newVisibilities[index];
setVisibilities(newVisibilities);
};
return (
<div className="App">
{data.map((value, index) => (
<h1 data-index={index} onClick={handleClick} className={visibilities[index] ? "selected" : undefined}>
Hello {value}, you are {visibilities[index] ? "visible" : "hidden"}!
</h1>
))}
</div>
);
}
ReactDOM.render(<App />, document.querySelector("main"));
.selected {
background: lightgreen;
}
h1 {
cursor: pointer;
user-select: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.production.min.js"></script>
<main></main>
Upvotes: 3
Reputation: 891
You can change the way you are structing the state to an array, then the click function will turn it invisible by index
const data = ["James", "John", "Jessica", "Jamie"];
export default function App() {
const [headersVisible, setHeaderVisible] = useState(
data.map(() => "visible")
);
const clicked = (index) => {
setHeaderVisible(
headersVisible.map((val, ix) => (index === ix ? "invisible" : val))
);
};
return (
<div className="App">
<h1>Sample Project</h1>
{data.map((value, index) => (
<h1
key={index}
className={headersVisible[index]}
onClick={()=>clicked(index)}
>
{/* classname should be the state "header0" but right now its just a string */}
Hello {value}!
</h1>
))}
</div>
);
}
Upvotes: 0
Reputation: 597
I'm afraid that is not a nice approach, but you can try this:
import React, { useState, useEffect } from "react";
import "./styles.css";
const data = ["James", "John", "Jessica", "Jamie"];
export default function App() {
const headers = [];
[headers[0], headers[1], headers[2], headers[3]] = [
useState("visable"),
useState("visable"),
useState("visable"),
useState("visable")
]; // each headers[index] is an array with [state, setState function]
const clicked = (index) => {
alert("clicked " + index);
headers[index][1]("invisible");
/*Is there an alternative like {setHeader + index} instead of this loop?*/
};
return (
<div className="App">
<h1>Sample Project</h1>
{data.map((value, index) => (
<h1
className={headers[index][0]}
onClick={() => {
clicked(index);
}}
>
{/* classname should be the state "header0" but right now its just a string */}
Hello {value}!
</h1>
))}
</div>
);
}
the code pen: https://codesandbox.io/s/agitated-rain-o31ms
Upvotes: 2
Reputation: 891
You are declaring 2 arguments in clicked function so e
is the actual index.
Shoud be
const clicked = (index) => {
Upvotes: 2