2016-10-06 3 views
3

Diese Frage könnte ein wenig auf der Seite einer "Best Practice" Frage fallen, aber bitte ertragen Sie mit mir. HierErsetze Objekt in Array auf reagieren Zustand

ist ein Teil meines Staates:

this.state = { 
    typeElements: { 
    headers: [ 
     { 
      name: "h1", 
      size: 70, 
      lineHeight: 1.25, 
      kearning: 0, 
      marginAfter: 0 
     }, { 
      name: "h2", 
      size: 70, 
      lineHeight: 1.25, 
      kearning: 0, 
      marginAfter: 0 
     }, { 
      name: "h3", 
      size: 70, 
      lineHeight: 1.25, 
      kearning: 0, 
      marginAfter: 0 
     }... 

Was muss ich tun, ist REPLACE das Objekt zu einem bestimmten Index auf der Header-Array.

Ich weiß nicht, wie das mit der Methode setState wie in this.setState(headers[1] = {obj}) zu tun - aber das ist offensichtlich ungültig. Meine aktuelle Methode wird ein neues Array erstellen und die alten wie dieser clobbering:

_updateStyle(props) { 
    let newState = Object.assign({}, this.state) 
    newState.typeElements.headers[props.index] = props 
    this.setState(newState) 
}; 

Für mein kleines Hacky Projekt ich denke, es ist in Ordnung, aber ich fühle mich wie diese super heavy handed ist und schnell auf jedem zu Performance-Problemen führen würde Art der Skala.

Antwort

5

Aktualisiert: da diese Antwort immer noch upvotes, beachten Sie, dass die vorherige Antwort unten mit modernen JavaScript und React veraltet ist. Das "Update" Addon ist nun veraltet und "immutability-helper" kann stattdessen verwendet werden.

Die React-Dokumente erwähnen auch warum immutability is important so mutieren Zustand vermeiden. Für unveränderliche Updates können Sie Object.assign() oder spread syntax verwenden, die für jede Verschachtelungsebene durchgeführt werden müssen, wie in diesem Beispiel das verschachtelte headers Objekt und seine Array-Elemente. In diesem speziellen Beispiel können wir den Array-Index als Schlüssel verwenden, so dass es auch möglich ist, den Spread-Operator zu verwenden, um einen flachen Klon des Arrays zu erstellen und ein neues Objekt als Wert bei gegebenem Index im geklonten Array zuzuordnen.

_updateStyle (props) { 
    const { typeElements } = this.state; 
    const updatedHeaders = [...typeElements.headers]; 
    updatedHeaders[props.index] = props; 
    this.setState({ 
    ...this.state, 
    typeElements: { 
     ...typeElements, 
     headers: updatedHeaders 
    } 
)); 
} 

Eine andere Lösung, die benötigt nicht die Ausbreitung Syntax erforderlich und wird, wenn wir den Array-Index nicht verwenden das Objekt, das wir ersetzen wollen zu finden, array.map wird mit einem neuen Array zu erstellen und das neue Objekt zurückkehr anstelle des alten bei gegebenem Index.

const updatedHeaders = typeElements.headers.map((obj, index) => { 
    return index === props.index ? props : obj; 
    }); 

ähnliche Beispiele in dem Redux docs erklären auch "immutable update patterns".

Reagieren hat einige Unveränderlichkeit Helfer für diese, die in die Dokumentation erklärt: https://facebook.github.io/react/docs/update.html

In Ihrem Fall, dass Sie die $ Spleiß-Befehl verwenden, könnte ein Element und fügen Sie den neuen bei bestimmten Index entfernen zum Beispiel:

_updateStyle (props) { 
    this.setState(update(this.state.typeElements, 
    { $splice: [[props.index, 1, props]] } 
)); 
} 
+0

aha, ich war mir dessen bewusst, aber war mir nicht sicher, es zu implementieren, also geht es in der setState-Methode? Was macht die "1"? Ist das der Einfügepunkt des neuen Objekts? Könnte ich nicht nochmal props.index dafür verwenden? – motleydev

+1

Schauen Sie sich die array.splice() Methode https://developer.mozilla.org/nl/docs/Web/JavaScript/Reference/Global_Objects/Array/splice an und Sie werden verstehen ... das Array enthält die an die Spleißmethode, also ist die "1" in diesem Beispiel deleteCount. –

2

Sie können die ES6 spread syntax verwenden, um einen Klon Ihrer Statuseigenschaft zu erstellen und ihn entsprechend zu aktualisieren, wobei der Zustand der Komponente intakt bleibt.

constructor() { 
    this.state = { 
    typeElements: { 
     headers: [ 
     { 
      name: "h1", 
      size: 70, 
      lineHeight: 1.25, 
      kearning: 0, 
      marginAfter: 0 
     }, { 
      name: "h2", 
      size: 70, 
      lineHeight: 1.25, 
      kearning: 0, 
      marginAfter: 0 
     }, { 
      name: "h3", 
      size: 70, 
      lineHeight: 1.25, 
      kearning: 0, 
      marginAfter: 0 
     } 
     ] 
    } 
    }; 
} 

... 

someOtherMethod() { 
    // makes a copy of the 'headers' array 
    const updatedHeadersArray = [...this.state.typeElements.headers]; 

    // say you want to update the 'name' property of the 
    // first element of the 'headers' array doing an in-place update 
    updatedHeadersArray[0].name = "updatedh1"; 

    // sets the new state by making a copy of the state and merges it with the rest of the arguments 
    this.setState({ 
    ...this.state, 
    typeElements: { 
     headers: updatedHeadersArray 
    } 
    }); 
} 
+2

Das ist nicht richtig. Wenn Sie das tun: '[... this.state.typeElements.headers]' Sie Verweise auf komplexe Objekte kopiert werden, so dass, wenn Sie tun: 'updateHeadersArray [0] .name‚= udpatedh1'' Sie ändern tatsächlich die Objekte, die in diesem Zustand leben. Diese Technik funktioniert nur für einfache Arrays einzelner Werte (z. B. String, Zahl) und nicht für Objekte. Überprüfen Sie diese grobe Geige: https://jsfiddle.net/4b69Lfhh/1/ –

+0

Eigentlich kann diese Lösung verwendet werden und wenn es richtig gemacht wird, ist es wahrscheinlich die beste Lösung in diesem Fall. Ich habe meine Antwort aktualisiert, um mögliche Lösungen mit aktuellem JavaScript zu erklären. Was Sie tun, ist 'spreadedHeaders [0] .name = 'sehen was ich meine?';' Was tatsächlich eine Eigenschaft des Objekts im Array mutiert, aber was es tun soll, ist das gesamte Objekt zu ersetzen, also a zuweisen neuer Wert bei gegebenem Index wie 'spreadedHeaders [0] = {}' –

0

Ich habe Ihre Idee. Soweit ich weiß setState() Methode kann den Array Kind Inhalt nicht ändern. Verwenden Sie stattdessen diese Methode, ändern Sie sie einfach direkt.

this.state.typeElements.headers[theIndex] = value; 

aber, indem Sie es direkt ändern, wird die Komponente nicht erneut rendern. Sie können das erneute Rendern mit dieser Methode erzwingen.

Hinweis: aber seien Sie vorsichtig, diese Praxis gilt als schlecht, Sie müssen sicherstellen, dass Ihr Zustand unveränderlich ist. Hier immutable array reference

0

Object.Assign verwendet flache Kopie, nicht tiefe Kopie.

Bitte beachten Sie, dass in Ihrem Beispiel Object.assign({}, this.state) Kopien nur Links auf die nächsten Kinder der state, das heißt zu typeElements, aber headers Array wird nicht kopiert.

Es gibt einen syntaktischen Zucker ... in ES6 für Object.Assign, Babel hat das spezielle Addon transform-object-rest-spread.

+0

Wirklich? Es hat tatsächlich auch die Header für mich kopiert. Kannst du es ausarbeiten? – motleydev

+0

@motleydev Die Object.assign() -Methode kopiert nur enumerable und __own__ Eigenschaften von einem Quellobjekt in ein Zielobjekt (MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ Objekt/zuweisen). Das bedeutet, dass nur Referenzen für die nächsten Objekte oder Werte für Primitive (Zahlen, Strings) kopiert werden. Sie erhalten das alte Array in Ihrer Implementierung, und wenn Sie es mutieren, wird es sich auch auf andere Stellen auswirken, an denen es verwendet wird. – Artru

+0

Oh, das ist nicht gut. Danke für den Zeiger! – motleydev

0

Bietet eine bessere Erklärung, wie dies zu erreichen ist.

  • Suchen Sie zuerst den Index des Elements, das Sie ersetzen, im Statusarray.
  • Zweitens update das Element an diesem Index
  • Drittens rufen setState mit der neuen Kollektion
import update from 'immutability-helper'; 

// this.state = { employees: [{id: 1, name: 'Obama'}, {id: 2, name: 'Trump'}] } 

updateEmployee(employee) { 
    const index = this.state.employees.findIndex((emp) => emp.id === employee.id); 
    const updatedEmployees = update(this.state.employees, {$splice: [[index, 1, employee]]}); // array.splice(start, deleteCount, item1) 
    this.setState({employees: updatedEmployees}); 
} 
Verwandte Themen