reactjs - React Hooks: Kan inte tilldela till skrivskyddad egenskap


Jag försöker uppdatera ett objekt som skapats med React.createElement(). Egenskapen jag försöker uppdatera är particleCopy.props.style.top. Nedan är min kod.

import React, { useState } from 'react';
import './Particles.scss';

export default function Particles() {
  const particleStyle = { color: 'blue', position: 'absolute', top: '0px' };
  const [particle, setParticle] = useState(
    React.createElement('div', { style: particleStyle }, '*')
  );

  const moveParticleDown = (particle, top) => {
    const particleCopy = Object.assign({}, particle);
    particleCopy.props.style.top = top + 'px';
    setParticle(particleCopy);
  };

  return (
    <div className="particles_container">
      <div className="particles">{particle}</div>
      <div className="controls">
        <button onClick={() => moveParticleDown(particle, 10)}>down</button>
      </div>
    </div>
  );
}

Jag får följande fel

Uncaught TypeError: Cannot assign to read only property 'top' of object '#<Object>'

Svar #1

Din particleCopy-funktion skapar bara en ytlig kopia av particle, vilket betyder att du försöker ändra objektet som particle pekar på. Du kan antingen titta på ett bibliotek för djupkopiering eller, vad som verkar mer idiomatiskt i React nuförtiden, sprida objektet så långt ner som du behöver:

const particleCopy = {
  ...particle,
  props: {
    ...particle.props,
    style: {
      ...particle.props.style,
      top: top + 'px'
    }
  }
}
setParticle(particleCopy);

Svar #2

Jag sökte faktiskt igenom internet för att se om det är rekommenderat att du lägger till element i state. Jag tror inte att det är så här du borde implementera det beteendet i React. Nåväl, om det fungerar, kommer jag att ha ett öppet sinne. Hittade en annan fråga som tar upp denna diskussion (kolla andra kommentaren): Lägg till element i ett state i React

För att inte tala om att göra en kopia, kan det vara dyrt.

Varför inte spara positionen som tillstånd?

export default function Particles() {
  const particleStyle = { color: 'blue', position: 'absolute', top: '0px' };
  // if ever you need to track other than top
  const [position, setPosition] = useState({top: 0, bottom: 0, left: 0, right: 0});

  const moveParticleDown = p => {
    setPosition({top: p});
  };

  return (
    <div className="particles_container">
      <div className="particles">
        <div style={...particleStyle, top: `${position.top}px`} /> 
      </div>
      <div className="controls">
        <button onClick={() => moveParticleDown(10)}>down</button>
      </div>
    </div>
  );
}