Kann ich eine Funktion ausführen, nachdem die Aktualisierung von setState abgeschlossen ist?


174

Ich bin sehr neu in React JS (wie in, gerade erst heute begonnen). Ich verstehe nicht ganz, wie setState funktioniert. Ich kombiniere React und Easel JS, um ein Raster basierend auf Benutzereingaben zu zeichnen. Hier ist mein JS-Bin: http://jsbin.com/zatula/edit?js,output

Hier ist der Code:

        var stage;

    var Grid = React.createClass({
        getInitialState: function() {
            return {
                rows: 10,
                cols: 10
            }
        },
        componentDidMount: function () {
            this.drawGrid();
        },
        drawGrid: function() {
            stage = new createjs.Stage("canvas");
            var rectangles = [];
            var rectangle;
            //Rows
            for (var x = 0; x < this.state.rows; x++)
            {
                // Columns
                for (var y = 0; y < this.state.cols; y++)
                {
                    var color = "Green";
                    rectangle = new createjs.Shape();
                    rectangle.graphics.beginFill(color);
                    rectangle.graphics.drawRect(0, 0, 32, 44);
                    rectangle.x = x * 33;
                    rectangle.y = y * 45;

                    stage.addChild(rectangle);

                    var id = rectangle.x + "_" + rectangle.y;
                    rectangles[id] = rectangle;
                }
            }
            stage.update();
        },
        updateNumRows: function(event) {
            this.setState({ rows: event.target.value });
            this.drawGrid();
        },
        updateNumCols: function(event) {
            this.setState({ cols: event.target.value });
            this.drawGrid();
        },
        render: function() {
            return (
                <div>
                    <div className="canvas-wrapper">
                        <canvas id="canvas" width="400" height="500"></canvas>
                        <p>Rows: { this.state.rows }</p>
                        <p>Columns: {this.state.cols }</p>
                    </div>
                    <div className="array-form">
                        <form>
                            <label>Number of Rows</label>
                            <select id="numRows" value={this.state.rows} onChange={ this.updateNumRows }>
                                <option value="1">1</option>
                                <option value="2">2</option>
                                <option value ="5">5</option>
                                <option value="10">10</option>
                                <option value="12">12</option>
                                <option value="15">15</option>
                                <option value="20">20</option>
                            </select>
                            <label>Number of Columns</label>
                            <select id="numCols" value={this.state.cols} onChange={ this.updateNumCols }>
                                <option value="1">1</option>
                                <option value="2">2</option>
                                <option value="5">5</option>
                                <option value="10">10</option>
                                <option value="12">12</option>
                                <option value="15">15</option>
                                <option value="20">20</option>
                            </select>
                        </form>
                    </div>    
                </div>
            );
        }
    });
    ReactDOM.render(
        <Grid />,
        document.getElementById("container")
    );

Sie können im JS-Bin sehen, dass beim ersten Ändern der Anzahl der Zeilen oder Spalten mit einem der Dropdowns nichts passiert. Wenn Sie das nächste Mal einen Dropdown-Wert ändern, zeichnet das Raster die Zeilen- und Spaltenwerte des vorherigen Status. Ich vermute, dass dies geschieht, weil meine Funktion this.drawGrid () ausgeführt wird, bevor setState abgeschlossen ist. Vielleicht gibt es noch einen anderen Grund?

Vielen Dank für Ihre Zeit und Hilfe!

Antworten:


448

setState(updater[, callback]) ist eine asynchrone Funktion:

https://facebook.github.io/react/docs/react-component.html#setstate

Sie können eine Funktion ausführen, nachdem setState beendet wurde, indem Sie den zweiten Parameter callbackwie folgt verwenden :

this.setState({
    someState: obj
}, () => {
    this.afterSetStateFinished();
});

32
oder einfach this.setState ({someState: obj}, this.afterSetStateFinished);
Aleksei Prokopov

Ich mag hinzufügen , dass in meinem speziellen Beispiel, ich wahrscheinlich nennen will this.drawGrid()in componentDidUpdatewie @kennedyyu vorgeschlagen, weil zu jeder Zeit setStatebeendet ist, werde ich das Gitter neu gezeichnet werden soll (so würde dies einige Zeilen Code speichern), aber erreichen die gleiche Sache .
Monalisa717

Tolle Antwort ... hat mir sehr geholfen
MoHammaD ReZa DehGhani

Das spart mir viel Zeit. Vielen Dank.
Nayan Dey

28

renderwird jedes Mal aufgerufen, wenn Sie setStatedie Komponente bei Änderungen erneut rendern. Wenn Sie Ihren Aufruf drawGriddorthin verschieben, anstatt ihn in Ihren update*Methoden aufzurufen , sollten Sie kein Problem haben.

Wenn das bei Ihnen nicht funktioniert, gibt es auch eine Überlastung setState, bei der ein Rückruf als zweiter Parameter verwendet wird. Sie sollten dies als letzten Ausweg nutzen können.


2
Vielen Dank - Ihr erster Vorschlag funktioniert und (meistens) sinnvoll. Ich habe dies getan: render: function() { this.drawGrid(); return......
monalisa717

3
Ähm, bitte tu das nicht in render()... sytolks Antwort sollte die akzeptierte sein
mfeineis

Dies ist eine falsche Antwort. SetState wechselt zur Infintie-Schleife und stürzt die Seite ab.
Maverick

Justin, ich bin daran interessiert, warum der Rückruf von setStateals letztes Mittel verwendet werden sollte. Ich stimme den meisten Leuten hier zu, dass es sinnvoll ist, diesen Ansatz zu verwenden.
monalisa717

1
@Rohmer Dies ist viel zu teuer für die Ausführung bei jedem Renderaufruf, während es offensichtlich auch nicht bei jedem Aufruf benötigt wird. Wenn es rein reactwäre, würde das vdom in den meisten Fällen dafür sorgen, dass nicht zu viel Arbeit geleistet wird. Dies ist eine Interop mit einer anderen Bibliothek, die Sie minimieren möchten
mfeineis

14

Etwas herstellen setStateRückkehr einPromise

Zusätzlich zur Übergabe einer callbackto- setState()Methode können Sie sie um eine asyncFunktion wickeln und die then()Methode verwenden. In einigen Fällen kann dies zu einem saubereren Code führen:

(async () => new Promise(resolve => this.setState({dummy: true}), resolve)()
    .then(() => { console.log('state:', this.state) });

Und hier können Sie noch einen Schritt weiter gehen und eine wiederverwendbare setStateFunktion erstellen , die meiner Meinung nach besser ist als die obige Version:

const promiseState = async state =>
    new Promise(resolve => this.setState(state, resolve));

promiseState({...})
    .then(() => promiseState({...})
    .then(() => {
        ...  // other code
        return promiseState({...});
    })
    .then(() => {...});

Dies funktioniert gut in React 16.4, aber ich habe es in früheren Versionen von React noch nicht getestet .

Erwähnenswert ist auch, dass es in den meisten - wahrscheinlich allen Fällen - besser ist , Ihren Rückrufcode in der componentDidUpdateMethode zu belassen.


11

Wenn neue Requisiten oder Zustände empfangen werden (wie Sie setStatehier anrufen ), ruft React einige Funktionen auf, die aufgerufen werden componentWillUpdateundcomponentDidUpdate

Fügen Sie in Ihrem Fall einfach eine componentDidUpdateaufzurufende Funktion hinzuthis.drawGrid()

Hier ist Arbeitscode in JS Bin

Wie bereits erwähnt, wird im Code danach componentDidUpdateaufgerufenthis.setState(...)

dann wird componentDidUpdatedrinnen anrufenthis.drawGrid()

Weitere Informationen zum Komponentenlebenszyklus finden Sie unter Reagieren unter https://facebook.github.io/react/docs/component-specs.html#updating-componentwillupdate


anstatt nur den Link einzufügen. Bitte fügen Sie die entsprechenden Teile in die Antwort ein.
Phoenix
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.