Skip to content
Advertisement

REACT : getting error while re-rendering components using setState hook

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);
}

Advertisement

Answer

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);
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement