При перезаписи массива он перезаписывает себя в 2 раза больше. Как это исправить ? 'keyup' event react js

Всем привет! Я не давно начал изучать react ) Создаю свою мини игру по персонажу из Dota2 "Invoker". Суть в том что бы прожать правильную комбо сфер для заклинаний. Я записываю это комбо в useState массив const [ spells, setSpells] = useState(['', '', '']) для того что бы отображать какие сферы уже были нажаты. Когда срабатывает функция changeShperes массив перезаписывается в 2 раза больше. Будто за ним тянется его история перезаписей. Как это исправить ? И за за чего это возникает ?

import React, { useState } from 'react';
import Spell from '../UI/Spell/Spell';
// ======= Styles ======
import cl from './ActiveSpheres.module.scss';


const ActiveSpheres = ({ binds }) => {

  // binds = {
  //   'firstSpell': 'q',
  //   'secondSpell': 'w',
  //   'thirdSpell': 'e',
  //   'fourthSpell': 'd',
  //   'fifthSpell': 'f',
  //   'sixthSpell': 'r',
  // };

  const [ spells, setSpells] = useState(['', '', ''])


  const changeShperes = (e) => {

    let arr = spells;
    let sphere = '';

    if ( e.key === binds.firstSpell ) {
      // ======= quas ======
      if ( spells.length === 3) {
        arr.shift()
      }
      sphere = 'quas'
      
    } else if ( e.key === binds.secondSpell ) {
      // ======= wex ======
      if ( spells.length === 3) {
        arr.shift()
      }
      sphere = 'wex'
      
    } else if ( e.key === binds.thirdSpell ) {
      // ======= exort ======
      if ( spells.length === 3) {
        arr.shift()
      }
      sphere = 'exort'

    }
    setSpells([...arr, sphere])
    
  }



  document.addEventListener('keyup', changeShperes)

  return (
    <div className={cl.inner}>
      <div className={cl['active-spheres']}>

        {
          spells.map((sphere, indx) => (
            <Spell
              key={sphere + indx}
              pathImg={sphere}
            />
          ))
        }


      </div>
    </div>

  );
};

export default ActiveSpheres;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>


Ответы (2 шт):

Автор решения: Илья Паймушкин

Я бы посоветовал переделать следующим образом:

const changeShperes = (e) => {
    let sphere = '';

    if ( e.key === binds.firstSpell ) {
      sphere = 'quas'
    } else if ( e.key === binds.secondSpell ) {
      sphere = 'wex'
    } else if ( e.key === binds.thirdSpell ) {
      sphere = 'exort'
    }
    setSpells(spells => {
        return [sphere, ...spells.slice(0, 2)];
    })
    
  }
→ Ссылка
Автор решения: Kirill

Я понял, чего не хватало в моем коде. Это удаление event, чтобы он не плодился при каждом новом ренедере компонента.

Кому интересно можете почитать:

componentWillUnmount

componentDidMount

  const [ spells, setSpells] = useState(['', '', ''])

  const changeShperes = (e) => {
    let sphere = '';

    if ( e.key === binds.firstSpell ) {
      sphere = 'quas'
    } else if ( e.key === binds.secondSpell ) {
      sphere = 'wex'
    } else if ( e.key === binds.thirdSpell ) {
      sphere = 'exort'
    }

    if ( spells.length === 3 ) {
      spells.shift()
    }

    setSpells([...spells, sphere])
    
  }

  // === componentDidMount()
  useEffect(() => {
    document.addEventListener('keyup', changeShperes)
  }, [])
  
  // === componentWillUnmount()
  useEffect(() => {
    document.addEventListener('keyup', changeShperes)
    return () => {
      document.removeEventListener('keyup', changeShperes)
    }
  }, [spells])

  return (
    <div className={cl.inner}>
      <div className={cl['active-spheres']}>

        {
          spells.map((sphere, indx) => (
            <Spell
              key={sphere + indx}
              pathImg={sphere}
            />
          ))
        }


      </div>
    </div>

  );
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

→ Ссылка