Javascript 我可以在 setState 更新完成后执行函数吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34687091/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 16:44:34  来源:igfitidea点击:

Can I execute a function after setState is finished updating?

javascriptreactjssetstate

提问by monalisa717

I am very new to React JS (as in, just started today). I don't quite understand how setState works. I am combining React and Easel JS to draw a grid based on user input. Here is my JS bin: http://jsbin.com/zatula/edit?js,output

我对 React JS 很陌生(就像今天刚开始一样)。我不太明白 setState 是如何工作的。我结合 React 和 Easel JS 来根据用户输入绘制网格。这是我的 JS bin:http: //jsbin.com/zatula/edit?js, output

Here is the 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")
    );

You can see in the JS bin when you change the number of rows or columns with one of the dropdowns, nothing will happen the first time. The next time you change a dropdown value, the grid will draw to the previous state's row and column values. I am guessing this is happening because my this.drawGrid() function is executing before setState is complete. Maybe there is another reason?

当您使用其中一个下拉列表更改行数或列数时,您可以在 JS bin 中看到,第一次不会发生任何事情。下次更改下拉值时,网格将绘制到前一个状态的行和列值。我猜这是因为我的 this.drawGrid() 函数在 setState 完成之前执行。也许还有其他原因?

Thanks for your time and help!

感谢您的时间和帮助!

回答by sytolk

setState(updater[, callback])is an async function:

setState(updater[, callback])是一个异步函数:

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

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

You can execute a function after setState is finishing using the second param callbacklike:

您可以在 setState 完成后使用第二个参数执行函数,callback例如:

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

回答by Justin Niessner

renderwill be called every time you setStateto re-render the component if there are changes. If you move your call to drawGridthere rather than calling it in your update*methods, you shouldn't have a problem.

rendersetState如果有更改,每次重新渲染组件时都会调用。如果您将调用移到drawGrid那里而不是在您的update*方法中调用它,那么您应该没有问题。

If that doesn't work for you, there is also an overload of setStatethat takes a callback as a second parameter. You should be able to take advantage of that as a last resort.

如果这对您不起作用,那么还有一个setState将回调作为第二个参数的重载。您应该能够利用它作为最后的手段。

回答by Mahdi

Making setStatereturn a Promise

制作setState回报Promise

In addition to passing a callbackto setState()method, you can wrap it around an asyncfunction and use the then()method -- which in some cases might produce a cleaner code:

除了传递callbacktosetState()方法之外,您还可以将它包装在一个async函数周围并使用该then()方法——这在某些情况下可能会产生更清晰的代码:

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

And here you can take this one more step ahead and make a reusable setStatefunction that in my opinion is better than the above version:

在这里你可以更进一步,制作一个可重用的setState函数,在我看来它比上面的版本更好:

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

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

This works fine in React16.4, but I haven't tested it in earlier versions of Reactyet.

这在React16.4 中运行良好,但我还没有在React 的早期版本中测试它。

Also worth mentioning that keeping your callbackcode in componentDidUpdatemethod is a better practice in most -- probably all, cases.

还值得一提的是,在大多数情况下(可能是所有情况),将回调代码保留在componentDidUpdate方法中是一种更好的做法。

回答by Kennedy Yu

when new props or states being received (like you call setStatehere), React will invoked some functions, which are called componentWillUpdateand componentDidUpdate

当接收到新的 props 或状态时(就像你setState在这里调用的那样),React 会调用一些函数,这些函数被调用componentWillUpdatecomponentDidUpdate

in your case, just simply add a componentDidUpdatefunction to call this.drawGrid()

在您的情况下,只需添加一个componentDidUpdate函数即可调用this.drawGrid()

here is working code in JS Bin

这是JS Bin 中的工作代码

as I mentioned, in the code, componentDidUpdatewill be invoked after this.setState(...)

正如我所提到的,在代码中,componentDidUpdate将在之后调用this.setState(...)

then componentDidUpdateinside is going to call this.drawGrid()

然后componentDidUpdate里面会打电话this.drawGrid()

read more about component Lifecyclein React https://facebook.github.io/react/docs/component-specs.html#updating-componentwillupdate

阅读有关React 中组件生命周期的更多信息https://facebook.github.io/react/docs/component-specs.html#updating-componentwillupdate