Korrigieren Sie die Änderung von Statusarrays in ReactJS

415

Ich möchte am Ende eines stateArrays ein Element hinzufügen. Ist dies der richtige Weg, dies zu tun?

this.state.arrayvar.push(newelement);
this.setState({arrayvar:this.state.arrayvar});

Ich befürchte, dass das Ändern des Arrays an Ort und Stelle pushProbleme verursachen könnte - ist es sicher?

Die Alternative, eine Kopie des Arrays zu erstellen, und setStatedas scheint verschwenderisch.

Fadedbee
quelle
9
Ich denke, Sie können React Unveränderlichkeitshelfer verwenden. siehe dies: facebook.github.io/react/docs/update.html#simple-push
nilgun
setState in State Array Überprüfen Sie meine Lösung. <br/> <br> stackoverflow.com/a/59711447/9762736
Rajnikant Lodhi

Antworten:

763

In den React-Dokumenten heißt es:

Behandle diesen Zustand so, als wäre er unveränderlich.

Sie pushwerden den Status direkt mutieren und dies könnte möglicherweise zu fehleranfälligem Code führen, selbst wenn Sie den Status danach erneut "zurücksetzen". F.ex, es könnte dazu führen, dass einige Lebenszyklusmethoden wie componentDidUpdatenicht ausgelöst werden.

In späteren React-Versionen wird empfohlen , beim Ändern von Status eine Updater- Funktion zu verwenden, um Rennbedingungen zu vermeiden:

this.setState(prevState => ({
  arrayvar: [...prevState.arrayvar, newelement]
}))

Die Speicherverschwendung ist kein Problem im Vergleich zu den Fehlern, die bei nicht standardmäßigen Statusänderungen auftreten können.

Alternative Syntax für frühere React-Versionen

Sie können verwenden concat, um eine saubere Syntax zu erhalten, da ein neues Array zurückgegeben wird:

this.setState({ 
  arrayvar: this.state.arrayvar.concat([newelement])
})

In ES6 können Sie den Spread Operator verwenden :

this.setState({
  arrayvar: [...this.state.arrayvar, newelement]
})
David Hellsing
quelle
8
Können Sie ein Beispiel dafür geben, wann eine Rennbedingung eintreten würde?
soundly_typed
3
@Qiming pushgibt die neue Array-Länge zurück, sodass dies nicht funktioniert. Auch setStateist async und reagieren können mehrere Zustandsänderungen in einem einzigen Durchlauf machen Warteschlange.
David Hellsing
2
@mindeavour sagt, Sie haben einen Animationsrahmen, der nach Parametern in this.state sucht, und eine andere Methode, die einige andere Parameter bei Statusänderungen ändert. Es kann einige Frames geben, in denen sich der Status geändert hat, die sich jedoch nicht in der Methode widerspiegeln, die auf die Änderung wartet, da setState asynchron ist.
David Hellsing
1
@ChristopherCamps Diese Antwort empfiehlt nicht, setStatezweimal aufzurufen . Sie zeigt zwei ähnliche Beispiele für das Festlegen eines Statusarrays, ohne es direkt zu mutieren.
David Hellsing
2
Eine einfache Möglichkeit, ein Statusarray heutzutage als unveränderlich zu behandeln, ist: let list = Array.from(this.state.list); list.push('woo'); this.setState({list});Ändern Sie es natürlich an Ihre Stilvorlieben.
Grundtage
132

Am einfachsten, wenn Sie verwenden ES6.

initialArray = [1, 2, 3];

newArray = [ ...initialArray, 4 ]; // --> [1,2,3,4]

Neues Array wird sein [1,2,3,4]

um Ihren Status in React zu aktualisieren

this.setState({
         arrayvar:[...this.state.arrayvar, newelement]
       });

Erfahren Sie mehr über die Array-Destrukturierung

Staatenlos
quelle
@Muzietto kannst du das näher erläutern?
StateLess
4
Der Kern dieser Frage besteht darin, den Reaktionsstatus zu ändern und keine Arrays zu ändern. Sie haben meinen Standpunkt selbst gesehen und Ihre Antwort bearbeitet. Dies macht Ihre Antwort relevant. Gut gemacht.
Marco Faustinelli
2
Ihre Fragen betreffen nicht direkt die OP-Frage
StateLess
1
@ChanceSmith: Es wird auch in StateLess- Antworten benötigt . Hängen Sie bei der Statusaktualisierung nicht vom Status selbst ab. Offizielles Dokument: reactjs.org/docs/…
Arcol
1
@ RayCoder protokollieren und überprüfen Sie den Wert von Arrayvar, es sieht so aus, als wäre es kein Array.
StateLess
57

Der einfachste Weg mit ES6:

this.setState(prevState => ({
    array: [...prevState.array, newElement]
}))
Ridd
quelle
Entschuldigung, in meinem Fall möchte ich ein Array in ein Array verschieben. tableData = [['test','test']]Nachdem mein neues Array geschoben wurde tableData = [['test','test'],['new','new']]. wie man diese @David und @Ridd
Johncy
@ Johncy Wenn Sie [['test','test'],['new','new']]versuchen möchten, versuchen:this.setState({ tableData: [...this.state.tableData, ['new', 'new']]
Ridd
this.setState({ tableData: [...this.state.tableData ,[item.student_name,item.homework_status_name,item.comments===null?'-':item.comments] ] });Es fügt das neue Array zweimal ein. this.state.tableData.push([item.student_name,item.homework_status_name,item.comments===null?'-':item.comments]);Es erreicht das gewünschte Ziel. aber es ist nicht die richtige Art, wie ich denke.
Johncy
26

React kann Batch-Updates durchführen. Daher besteht der richtige Ansatz darin, setState eine Funktion zur Verfügung zu stellen, mit der das Update durchgeführt wird.

Für das React-Update-Addon funktioniert Folgendes zuverlässig:

this.setState( state => update(state, {array: {$push: [4]}}) );

oder für concat ():

this.setState( state => ({
    array: state.array.concat([4])
}));

Das Folgende zeigt, was https://jsbin.com/mofekakuqi/7/edit?js,output als Beispiel dafür ist, was passiert, wenn Sie etwas falsch machen.

Der Aufruf von setTimeout () fügt drei Elemente korrekt hinzu, da React keine Aktualisierungen innerhalb eines setTimeout-Rückrufs stapelweise durchführt (siehe https://groups.google.com/d/msg/reactjs/G6pljvpTGX0/0ihYw2zK9dEJ ).

Der Buggy onClick fügt nur "Third" hinzu, der feste jedoch wie erwartet F, S und T.

class List extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      array: []
    }

    setTimeout(this.addSome, 500);
  }

  addSome = () => {
      this.setState(
        update(this.state, {array: {$push: ["First"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Second"]}}));
      this.setState(
        update(this.state, {array: {$push: ["Third"]}}));
    };

  addSomeFixed = () => {
      this.setState( state => 
        update(state, {array: {$push: ["F"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["S"]}}));
      this.setState( state => 
        update(state, {array: {$push: ["T"]}}));
    };



  render() {

    const list = this.state.array.map((item, i) => {
      return <li key={i}>{item}</li>
    });
       console.log(this.state);

    return (
      <div className='list'>
        <button onClick={this.addSome}>add three</button>
        <button onClick={this.addSomeFixed}>add three (fixed)</button>
        <ul>
        {list}
        </ul>
      </div>
    );
  }
};


ReactDOM.render(<List />, document.getElementById('app'));
NealeU
quelle
Gibt es wirklich einen Fall, in dem es passiert? Wenn wir es einfach tunthis.setState( update(this.state, {array: {$push: ["First", "Second", "Third"]}}) )
Albizia
1
@ Albizia Ich denke, Sie sollten einen Kollegen finden und mit ihm besprechen. Es gibt kein Stapelproblem, wenn Sie nur einen setState-Aufruf ausführen. Der Punkt ist zu zeigen, dass React Batch-Updates, also ja ... es gibt wirklich einen Fall, den Sie in der JSBin-Version des obigen Codes finden können. Fast alle Antworten in diesem Thread sprechen dies nicht an, so dass es eine Menge Code gibt, der manchmal schief geht
NealeU
state.array = state.array.concat([4])Dies mutiert das vorherige Statusobjekt.
Emile Bergeron
1
@EmileBergeron Vielen Dank für Ihre Ausdauer. Ich schaute schließlich zurück und sah, was mein Gehirn nicht sehen wollte, und überprüfte die Dokumente, also werde ich sie bearbeiten.
NealeU
1
Gut! Es ist wirklich leicht, sich zu irren, da die Unveränderlichkeit in JS nicht offensichtlich ist (umso mehr, wenn es um die API einer Bibliothek geht).
Emile Bergeron
17

Wie im Kommentar bei @nilgun erwähnt, können Sie die Helfer für die Reaktion auf Unveränderlichkeit verwenden . Ich fand das super nützlich.

Aus den Dokumenten:

Einfacher Druck

var initialArray = [1, 2, 3];
var newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]

initialArray ist immer noch [1, 2, 3].

Clarkie
quelle
6
Die Helfer für die Unveränderlichkeit von React werden in der Dokumentation als veraltet beschrieben. Stattdessen sollte jetzt github.com/kolodny/immutability-helper verwendet werden.
Periata Breatta
3

Wenn Sie eine Funktionskomponente verwenden, verwenden Sie diese bitte wie folgt.

const [chatHistory, setChatHistory] = useState([]); // define the state

const chatHistoryList = [...chatHistory, {'from':'me', 'message':e.target.value}]; // new array need to update
setChatHistory(chatHistoryList); // update the state
user3444748
quelle
1

Für das Hinzufügen eines neuen Elements zum Array push()sollte die Antwort sein.

Für das Entfernen des Elements und das Aktualisieren des Status des Arrays funktioniert der folgende Code für mich. splice(index, 1)kann nicht arbeiten.

const [arrayState, setArrayState] = React.useState<any[]>([]);
...

// index is the index for the element you want to remove
const newArrayState = arrayState.filter((value, theIndex) => {return index !== theIndex});
setArrayState(newArrayState);
Jagger
quelle
0

Ich versuche, einen Wert in einem Array-Status zu pushen und einen solchen Wert festzulegen und den Status-Array- und Push-Wert durch die Map-Funktion zu definieren.

 this.state = {
        createJob: [],
        totalAmount:Number=0
    }


 your_API_JSON_Array.map((_) => {
                this.setState({totalAmount:this.state.totalAmount += _.your_API_JSON.price})
                this.state.createJob.push({ id: _._id, price: _.your_API_JSON.price })
                return this.setState({createJob: this.state.createJob})
            })
Rajnikant Lodhi
quelle
0

Dies funktionierte für mich, um ein Array innerhalb eines Arrays hinzuzufügen

this.setState(prevState => ({
    component: prevState.component.concat(new Array(['new', 'new']))
}));
MadKad
quelle
0

Hier ist ein Beispiel aus dem Jahr 2020, Reactjs Hook, von dem ich dachte, dass es anderen helfen könnte. Ich verwende es, um einer Reactjs-Tabelle neue Zeilen hinzuzufügen. Lassen Sie mich wissen, ob ich etwas verbessern könnte.

Hinzufügen eines neuen Elements zu einer Funktionszustandskomponente:

Definieren Sie die Statusdaten:

    const [data, setData] = useState([
        { id: 1, name: 'John', age: 16 },
        { id: 2, name: 'Jane', age: 22 },
        { id: 3, name: 'Josh', age: 21 }
    ]);

Lassen Sie eine Schaltfläche eine Funktion auslösen, um ein neues Element hinzuzufügen

<Button
    // pass the current state data to the handleAdd function so we can append to it.
    onClick={() => handleAdd(data)}>
    Add a row
</Button>
function handleAdd(currentData) {

        // return last data array element
        let lastDataObject = currentTableData[currentTableData.length - 1]

        // assign last elements ID to a variable.
        let lastID = Object.values(lastDataObject)[0] 

        // build a new element with a new ID based off the last element in the array
        let newDataElement = {
            id: lastID + 1,
            name: 'Jill',
            age: 55,
        }

        // build a new state object 
        const newStateData = [...currentData, newDataElement ]

        // update the state
        setData(newStateData);

        // print newly updated state
        for (const element of newStateData) {
            console.log('New Data: ' + Object.values(element).join(', '))
        }

}
Ian Smith
quelle
-1
this.setState({
  arrayvar: [...this.state.arrayvar, ...newelement]
})
M. Samiei
quelle
2
Fügen Sie eine Erklärung hinzu, um zu vermeiden, dass dieser Beitrag als Beitrag von geringer Qualität von betrachtet wird stackoverflow.
Harsha Biyani
2
@Kobe diesen Aufruf "Spread Operator" in ES6 und Anhängen von Array2-Elementen an Array1. danke für die Abwertung (:
M.Samiei
@ M.Samiei Sie müssen Ihren Beitrag bearbeiten, um Abstimmungen zu vermeiden. Es ist von geringer Qualität als nur ein Code-Snippet. Es ist auch gefährlich, den Status auf diese Weise zu ändern, da Aktualisierungen möglicherweise gestapelt werden. Daher ist der bevorzugte Weg z. B.setState(state => ({arrayvar: [...state.arrayvar, ...newelement]}) );
NealeU
und das Verteilen des newelementObjekts innerhalb eines Arrays wirft TypeErrortrotzdem ein.
Emile Bergeron
-1
//------------------code is return in typescript 

const updateMyData1 = (rowIndex:any, columnId:any, value:any) => {

    setItems(old => old.map((row, index) => {
        if (index === rowIndex) {
        return Object.assign(Object.assign({}, old[rowIndex]), { [columnId]: value });
    }
    return row;
}));
ANUBHAV RAWAT
quelle
-6

Dieser Code funktioniert für mich:

fetch('http://localhost:8080')
  .then(response => response.json())
  .then(json => {
    this.setState({mystate: this.state.mystate.push.apply(this.state.mystate, json)})
  })
Hamid Hosseinpour
quelle
2
Asbar Ali
2
Der Status der Komponente kann nicht korrekt aktualisiert werden, da .pusheine Zahl und kein Array zurückgegeben wird.
Emile Bergeron