In my code, I am trying to retrieve background images depending on the background data on the object, however those backgrounds will also change depending on the state of the element.
First of all, my data array looks like this:
const Cube = [
{
name: "foo"
faces: [
{
data: [
[{bonus: "bar", bg: "bar2"}],
...
],
...
},
...
],
...
},
...
];
And this is how my App.js looks:
function App() {
const [cellState, setCellState] = useState("inactive");
return (
<div className="App">
<div id="cube-container">
{Cube[0].faces[0].data.map((row) => {
return row.map((cell, index) => {
return (
<img
src={require(`./assets/cube/Square_Cube_Icon/${cell.bg}${
cellState === "inactive" ? "_Unlit" : "_Partial"
}.png`)}
alt={`${cell.bg} background`}
className="cellItem"
onClick={() => setCellState("active")}
state={cellState}
key={index}
/>
);
});
})}
</div>
</div>
);
}
Question is, in the 4x4 grid output, if I click on any item, instead of changing the clicked elements background because of state change, it changes background of every cell, which should happen since the useState is shared between all of them. How can I make it so every element of the map function has their own state that I can update separately?
Found the solution:
Separating it as a component and passing key and cell values as props, then creating and manipulating the state inside fixed the issue.Selected answer also solves the issue.
Here's the solution for future readers.
App.js
function App() {
return (
<div className="App">
<div id="cube-container">
{CubeOfTruth[0].faces[0].data.map((row) => {
return row.map((cell, index) => {
return <CubeItem key={index} cell={cell} />;
});
})}
</div>
</div>
);
}
CubeItem.js
function CubeItem({ cell, index }) {
const [cellState, setCellState] = useState("inactive");
return (
<img
src={require(`../assets/cube/Square_Cube_Icon/${cell.bg}${
cellState === "inactive" || cellState === "partial" ? "_Unlit" : ""
}.png`)}
alt={`${cell.bg} background`}
className="cellItem"
onClick={() => setCellState("active")}
state={cellState}
key={index}
/>
);
}
CodePudding user response:
Here's an easy way to manage multiple states using an object with its keys representing your cells and the values representing whether it is active or inactive. This is also easy to generalize so if you add more cells then all you have to do is just add it to the initialState & rest of your code should work as is.
function App() {
// const [cellState, setCellState] = useState("inactive");
const initialState = {
bar1: "inactive",
bar2: "inactive",
bar3: "inactive",
bar4: "inactive",
}
const [cellState, SetCellState] = React.useState(initialState);
const handleSetCellState = (key) => {
setCellState({
...cellState,
[key]: "active",
});
};
return (
<div className="App">
<div id="cube-container">
{Cube[0].faces[0].data.map((row) => {
return row.map((cell, index) => {
return (
<img
src={require(`./assets/cube/Square_Cube_Icon/${cell.bg}${
cellState[cell.bg] === "inactive" ? "_Unlit" : "_Partial"
}.png`)}
alt={`${cell.bg} background`}
className="cellItem"
onClick={() => handleSetCellState(cell.bg)}
state={cellState[cell.bg]}
key={index}
/>
);
});
})}
</div>
</div>
);
}