javascript not React.js: setState sovrascrittura, non fusione




react setstate callback (3)

Sono abbastanza nuovo per react.js e sono in procinto di sperimentare costruendo un layout in stile muratura.

Rendo ogni elemento al DOM, quindi ho bisogno di scorrere su ogni elemento e applicare le posizioni x e y in base agli elementi precedenti.

Il modello iniziale ha questo aspetto:

[
  {
    "title": "The Forrest",
    "description": "some cool text",
    "imgSmallSrc": "/img/img4-small.jpg",
    "imgAlt": "Placeholder image",
    "tags": [
        "Design",
        "Mobile",
        "Responsive"
    ],
    "date": 1367154709885,
    "podStyle": {
      "width": 253
    }
  }
]

(Ho mostrato solo un elemento per mantenere le cose corte).

Una volta completato il ciclo e ho i miei dati x e y, voglio applicarlo all'oggetto podStyle. Chiamo setState con i seguenti dati:

[
  {
    "podStyle": {
      "x": 0,
      "y": 0,
      "height": 146,
      "width": 253
    }
  }
]

Questo sembra rimuovere tutti i dati correnti dal modello e lasciarmi solo con i dati di podStyle. Sto fraintendendo come funziona questa unione?

Grazie in anticipo per qualsiasi aiuto!


Answer #1

La fusione è poco profonda, quindi this.setState({point}) lascia (ed: this.state.radius) intatto, ma sostituisce completamente (ed: this.state.point).

https://facebook.github.io/react/docs/state-and-lifecycle.html#state-updates-are-merged

Per offrire una prospettiva ES7 + sulle risposte già fornite, utilizzare transform-object-rest-spread invece di Object.assign() :

class MyComponent extends React.Component {
    state = {
        point: { 
            x: 0, 
            y: 0,
        },
        radius: 10,
    }

    handleChange = () => {
        this.setState((prevState, props) => ({
            point: {
                // rest operator (...) expands out to:
                ...prevState.point, // x:0, y:0,
                y: 1, // overwrites old y
            },
            // radius is not overwritten by setState
        }));
    }

    render() {
        // omitted
    }
}

.babelrc (richiede anche proprietà transform-class-properties da babel preset stage 2)

{
    "presets": ["es2015", "stage-2", "react"],
    "plugins": ["transform-object-rest-spread"],
}

Aggiornato il 2018-04-22

Come sottolinea @sheljohn (grazie!), this.state a this.state all'interno di setState è inaffidabile:

Poiché this.props e this.state possono essere aggiornati in modo asincrono, non si deve fare affidamento sui loro valori per calcolare lo stato successivo.

...

Per risolverlo, usa una seconda forma di setState() che accetta una funzione piuttosto che un oggetto. Quella funzione riceverà lo stato precedente come primo argomento e gli oggetti di scena nel momento in cui l'aggiornamento viene applicato come secondo argomento

https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous


Answer #2

Se il tuo stato è un oggetto:

getInitialState: function() {
  return { x: 0, y: 0 };
}

puoi usare setState per impostare chiavi individuali su quell'oggetto:

this.setState({ x: 1 }); // y still == 0

Reagire non crea una fusione intelligente del tuo stato; per esempio, questo non funziona:

getInitialState: function() {
  return {
    point: { x: 0, y: 0 },
    radius: 10
  };
}

this.setState({point: {x: 1}});
// state is now == {point: {x: 1}, radius: 10} (point.y is gone)

[Modificare]

Come menzionato da @ssorallen, puoi utilizzare gli helper dell'immutabilità per ottenere l'effetto che stai cercando:

var newState = React.addons.update(this.state, {
  point: { x: {$set: 10} }
});
this.setState(newState);

Vedi questo JSFiddle per un esempio: http://jsfiddle.net/BinaryMuse/HW6w5/


Answer #3

Qualcosa di simile a:

getInitialState: function() {
    return {
        something: { x: 0, y: 0 },
        blah: 10
    };
}

var state = Object.assign(this.state, {
    something: Object.assign(this.state.something, { y: 50 }),
});

this.setState(state);

Sarebbe meglio se fosse ricorsivo / profondo piuttosto che codificare l'albero, ma lo lascerò al lettore :)





reactjs