Bisakah saya menjalankan fungsi setelah setState selesai memperbarui?

175

Saya sangat baru untuk Bereaksi JS (seperti pada, baru mulai hari ini). Saya tidak begitu mengerti bagaimana setState bekerja. Saya menggabungkan React dan Easel JS untuk menggambar kotak berdasarkan input pengguna. Inilah bin JS saya: http://jsbin.com/zatula/edit?js,output

Ini kodenya:

        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")
    );

Anda dapat melihat di tempat sampah JS ketika Anda mengubah jumlah baris atau kolom dengan salah satu dropdown, tidak ada yang terjadi pertama kali. Lain kali Anda mengubah nilai dropdown, kisi akan menarik ke nilai baris dan kolom negara sebelumnya. Saya menduga ini terjadi karena fungsi this.drawGrid () saya dieksekusi sebelum setState selesai. Mungkin ada alasan lain?

Terima kasih atas waktu dan bantuan Anda!

monalisa717
sumber

Jawaban:

450

setState(updater[, callback]) adalah fungsi async:

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

Anda dapat menjalankan fungsi setelah setState selesai menggunakan param kedua callbackseperti:

this.setState({
    someState: obj
}, () => {
    this.afterSetStateFinished();
});
rakyat jelata
sumber
33
atau hanya this.setState ({someState: obj}, this.afterSetStateFinished);
Aleksei Prokopov
Saya ingin menambahkan bahwa dalam contoh spesifik saya, saya mungkin ingin menelepon this.drawGrid()di componentDidUpdateseperti @kennedyyu disarankan, karena setiap saat setStateselesai, saya akan ingin redraw grid (jadi ini akan menghemat baris kode), tetapi mencapai hal yang sama .
monalisa717
Jawaban yang bagus ... banyak membantu saya
MoHammaD ReZa DehGhani
Ini menghemat banyak waktu untuk saya. Terima kasih.
Nayan Dey
28

renderakan dipanggil setiap kali Anda setStatemerender ulang komponen jika ada perubahan. Jika Anda memindahkan panggilan Anda ke drawGridsana daripada memanggilnya dalam update*metode Anda, Anda seharusnya tidak memiliki masalah.

Jika itu tidak berhasil untuk Anda, ada juga kelebihan setStateyang mengambil panggilan balik sebagai parameter kedua. Anda harus dapat memanfaatkan itu sebagai pilihan terakhir.

Justin Niessner
sumber
2
Terima kasih - saran pertama Anda berhasil dan (kebanyakan) masuk akal. Saya melakukan ini: render: function() { this.drawGrid(); return......
monalisa717
3
Ahem, tolong jangan lakukan ini di render()... jawaban sytolk harus yang diterima
mfeineis
Ini adalah jawaban yang salah, setState akan menggunakan infintie loop dan akan merusak halaman.
Maverick
Justin, saya tertarik mengapa kata panggilan balik setStateharus digunakan sebagai upaya terakhir. Saya setuju dengan kebanyakan orang di sini bahwa masuk akal untuk menggunakan pendekatan itu.
monalisa717
1
@Rohmer ini adalah cara mahal untuk mengeksekusi pada setiap panggilan render sementara itu jelas tidak diperlukan pada setiap panggilan juga. Jika itu murni reactvdom akan mengurus tidak melakukan terlalu banyak pekerjaan dalam banyak kasus, ini adalah interop dengan perpustakaan lain yang ingin Anda meminimalkan
mfeineis
14

Membuat setStatepengembalian aPromise

Selain meneruskan metode callbackke setState(), Anda dapat membungkusnya di sekitar asyncfungsi dan menggunakan then()metode - yang dalam beberapa kasus dapat menghasilkan kode pembersih:

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

Dan di sini Anda dapat mengambil satu langkah lebih maju dan membuat setStatefungsi yang dapat digunakan kembali yang menurut saya lebih baik daripada versi di atas:

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

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

Ini berfungsi dengan baik di React 16.4, tapi saya belum mengujinya di React versi sebelumnya .

Juga patut disebutkan bahwa menjaga kode panggilan balik Anda dalam componentDidUpdatemetode adalah praktik yang lebih baik di sebagian besar - mungkin semua, kasus.

Mahdi
sumber
11

ketika properti atau negara bagian baru diterima (seperti Anda menelepon di setStatesini), Bereaksi akan memanggil beberapa fungsi, yang disebut componentWillUpdatedancomponentDidUpdate

dalam kasus Anda, cukup tambahkan componentDidUpdatefungsi untuk meneleponthis.drawGrid()

di sini adalah kode yang berfungsi di JS Bin

seperti yang saya sebutkan, dalam kode, componentDidUpdateakan dipanggil setelahthis.setState(...)

maka componentDidUpdatedi dalam akan memanggilthis.drawGrid()

baca lebih lanjut tentang Siklus Hidup komponen dalam Bereaksi https://facebook.github.io/react/docs/component-specs.html#updating-componentwillupdate

Kennedy Yu
sumber
alih-alih menempelkan tautan. tolong tambahkan bagian yang relevan dalam jawaban.
phoenix