I have 6 cubes displayed on the page and there is a counter.
When you click on a cube, its background changes to red and the counter increases by one, but the counter does not want to increase. I always have it 1. But if you remove the setcub([...cubikinapole])
line, then the counter starts working as it should, but now the background does not change. What to do?
import "./styles.css";
import {useState} from 'react'
export default function App() {
let [cubikinapole, setcub] = useState([
{id:1,title:1, img:"https://www.zonkpro.ru/zonk/assets/dice/mini/1.png", style: 'none'},
{id:2,title:2, img:"https://www.zonkpro.ru/zonk/assets/dice/mini/2.png" , style: 'none'},
{id:3,title:3, img:"https://www.zonkpro.ru/zonk/assets/dice/mini/3.png" , style: 'none'},
{id:4,title:4, img:"https://www.zonkpro.ru/zonk/assets/dice/mini/4.png" , style: 'none'},
{id:5,title:5, img:"https://www.zonkpro.ru/zonk/assets/dice/mini/5.png" , style: 'none'},
{id:6,title:6, img:"https://www.zonkpro.ru/zonk/assets/dice/mini/6.png" , style: 'none'},
])
let [count,se] = useState(0)
function a(el) {
se(count )
el.style = 'active'
setcub([...cubikinapole])
console.log(count)
}
return (
<div className="App">
{cubikinapole.length !== 0 ?
cubikinapole.map((el)=>
<div id='cub' key={el.id} className={el.style} onClick={()=>a(el)}>
<img src={el.img} />
</div>
)
: console.log() }
</div>
);
}
CSS
.active{
background-color: red;
}
CodePudding user response:
you're never supposed to change the value of a useState by the value itself, only by the setter - what you're doing where you're writing se(count )
is giving the old value of the count to se
and then directly changing count by count ,
so it looks like a race condition, you can fix it by just changing it to se(count 1)
and your code will run as it should
CodePudding user response:
You can try this code. count
increases its value after the setCount
function is executed, so you should use count
to pass the increased value to the function. And you'd better use useEffect
to monitor the count
value.
import "./styles.css";
import { useState, useEffect } from "react";
export default function App() {
let [cubikinapole, setCub] = useState([
{
id: 1,
title: 1,
img: "https://www.zonkpro.ru/zonk/assets/dice/mini/1.png",
style: "none"
},
{
id: 2,
title: 2,
img: "https://www.zonkpro.ru/zonk/assets/dice/mini/2.png",
style: "none"
},
{
id: 3,
title: 3,
img: "https://www.zonkpro.ru/zonk/assets/dice/mini/3.png",
style: "none"
},
{
id: 4,
title: 4,
img: "https://www.zonkpro.ru/zonk/assets/dice/mini/4.png",
style: "none"
},
{
id: 5,
title: 5,
img: "https://www.zonkpro.ru/zonk/assets/dice/mini/5.png",
style: "none"
},
{
id: 6,
title: 6,
img: "https://www.zonkpro.ru/zonk/assets/dice/mini/6.png",
style: "none"
}
]);
let [count, setCount] = useState(0);
useEffect(() => {
console.log(count);
}, [count]);
function a(el) {
setCount( count);
el.style = "active";
setCub([...cubikinapole]);
}
return (
<div className="App">
{cubikinapole.length !== 0
? cubikinapole.map((el) => (
<div key={el.id} className={el.style} onClick={(e) => a(el)}>
<img src={el.img} alt={el.title} />
</div>
))
: console.log()}
</div>
);
}
Live demo: https://codesandbox.io/s/amazing-haze-0zdb26?file=/src/App.js:0-1369
CodePudding user response:
There are a few issues. Note: I've changed the names of some variable for clarity.
You can't set your counter like that. Ideally you want to take the previous state, and then update and return that. So
setCount(count )
becomessetCount(prev => prev = 1)
.id
s should be unique. If you want to uniquely identify an element you can use adata attribute
, and then pick up that value in the handler.With the
id
in hand in your handler you should find the object in state that where the objectid
matches theid
of the element you clicked on, and then update that. To do that make a copy of the state first, update the object, and then set the state using the changed copy.It's not really clear whether it was the background of the cube you wanted to change, or the image itself so I've just left your code as-is. For the actual image to change would be slightly more difficult.
const { useState } = React;
// For clarity I'm passing in the image
// config to the component, and then setting the
// cube state with it
function Example({ config }) {
// Initialise the states
const [ cubes, setCubes ] = useState(config);
const [ count, setCount ] = useState(0);
// When an element is clicked take the
// id from its data set. Make a copy of the state
// and then find the index of the object that has a
// matching id. Then update that object with the new
// style, and set both the cube and count states
function handleClick(e) {
const { id } = e.currentTarget.dataset;
const copy = [...cubes];
const index = copy.findIndex(cube => cube.id === Number(id));
if (index >= 0) copy[index].style = 'active';
setCubes(copy);
setCount(prev => prev = 1);
}
// I've added a count element here.
// `map` over state adding the data attribute
// to the div element.
return (
<div className="App">
<div className="counter">Count: {count}</div>
{config.map(obj => {
const { id, style, img } = obj;
return (
<div
key={id}
data-id={id}
className={style}
onClick={handleClick}
><img src={img} />
</div>
);
})}
</div>
);
}
const config=[{id:1,title:1,img:"https://www.zonkpro.ru/zonk/assets/dice/mini/1.png",style:"none"},{id:2,title:2,img:"https://www.zonkpro.ru/zonk/assets/dice/mini/2.png",style:"none"},{id:3,title:3,img:"https://www.zonkpro.ru/zonk/assets/dice/mini/3.png",style:"none"},{id:4,title:4,img:"https://www.zonkpro.ru/zonk/assets/dice/mini/4.png",style:"none"},{id:5,title:5,img:"https://www.zonkpro.ru/zonk/assets/dice/mini/5.png",style:"none"},{id:6,title:6,img:"https://www.zonkpro.ru/zonk/assets/dice/mini/6.png",style:"none"}];
ReactDOM.render(
<Example config={config} />,
document.getElementById('react')
);
.active { background-color: red; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>