I'm trying to change an inline style property of several items that have position info stored in a useState hook as well on the style property of each item rendered. I wanted to change the CSS value of all the items except the one I clicked on. I devised a function to do so, however when I try to update the CSS value using setState, the items don't re-render and I get an error saying "discs.map is not a function". Am I using the set state hook correctly? Should i be using the setState hook for a task like this i.e animating position of items?
// Main Component
function App() {
const [discs, setDiscs] = useState([
{ id: 1, top: 100 },
{ id: 2, top: 200 },
{ id: 3, top: 300 },
{ id: 4, top: 400 },
{ id: 5, top: 500 },
{ id: 6, top: 600 },
{ id: 7, top: 700 },
{ id: 8, top: 800 },
{ id: 9, top: 900 },
{ id: 10, top: 1000 },
{ id: 11, top: 1100 },
{ id: 12, top: 1300 }
])
function enlargeDisc(e, num) {
let t = e.target
if (t.classList.contains('active')) {
t.classList.remove('active')
adjustDiscPos(num, true)
} else {
t.classList.add('active')
adjustDiscPos(num, false)
}
}
function adjustDiscPos(n, backToOriginal) {
console.log(discs)
discs.forEach((disc) => {
if (disc.id < n) {
setDiscs(prevState => backToOriginal ? prevState 300 : prevState - 300)
}
if (disc.id > n) {
setDiscs(prevState => backToOriginal ? prevState - 300 : prevState 300)
}
})
}
return (
<div className="App">
<div className="container">
<div className="wrapper" style={{bottom: scroll}}>
{discs.map((item) => (
<div className ='disc' key={item.id} data-index={item.id} style={{top: item.top 'px'}} onClick={(e)=> enlargeDisc(e, item.id)} ></div>
))}
</div>
</div>
</div>
);
}
export default App;
CSS
.disc {
position: absolute;
transform:
translate(-50%, -50%) rotateX(90deg) scale(1);
width: 10em;
height: 10em;
transform-style: preserve-3d;
cursor: pointer;
background-image:
radial-gradient(rgba(0, 0, 0, 0), rgb(22, 22, 22) 95%),
url('./img/cd3.png');
background-size: 100% 100%;
transition: all 0.5s ease-in-out;
}
.disc.active {
transform:
translate(-50%, -50%) rotateX(0deg) scale(1.5);
}
CodePudding user response:
I am assuming here that it is the value of top
you wish to manipulate. I am also assuming that you wish to leave the disc with id equal to n
as it is. Correct me if I'm wrong.
What you need to do is to create a temporary array and add the new values to that array. Then, when that is done, you call setDiscs
with the temporary array as an argument.
let tmp_discs = [];
discs.forEach((disc) => {
if (disc.id < n) {
tmp_discs[disc.id] = backToOriginal ? disc.top 300 : disc.top - 300;
} else if (disc.id > n) {
tmp_discs[disc.id] = backToOriginal ? disc.top - 300 : disc.top 300;
} else {
tmp_discs[disc.id] = disc.top;
}
})
setDiscs(tmp_discs);
CodePudding user response:
In adjustDiscPos
you are calling setDiscs
with the value of a subtraction of an array and 300
prevState - 300
.
This will push NaN
to discs
and hence your error.
You should compute a new array in your forEach
and then call setDiscs
only once.
CodePudding user response:
Building up on what Radu Dita said
In adjustDiscPos you are calling setDiscs with the value of a subtraction of an array and 300 prevState - 300. This will push NaN to discs and hence your error. You should compute a new array in your forEach and then call setDiscs only once.
What this means is that instead of using a forEach
and do setDiscs
for each item in the array, you're supposed to use setDiscs
once (prevState
is the reference to the current discs
value), and do your arrayu operation on it through map
or something.
function adjustDiscPos(n, backToOriginal) {
console.log(discs)
setDiscs(prevState => prevState.map(disc => {
const offset = backToOriginal ? 300 : -300;
return { id: disc.id, top: disc.id < n ? disc.top offset : disc.top - offset };
})
Just make sure to remember not to mutate prevState
directly either since it is a reference to current state variable, but rather to use some array function which returns a new value.