React@16.x(5)核心概念-setState

React@16.x(5)核心概念-setState

码农世界 2024-05-28 后端 78 次浏览 0个评论

目录

  • 1,异步更新状态
    • 第2个参数
    • 2,多个 setState
      • 1,问题
      • 2,配合第2个参数解决
      • 3,第1个参数是函数
      • 4,同步更新状态举例

        最佳实践

        1. 把所有的 setState 当做异步的。
        2. 永远不要信任 setState 调用之后的状态(可能未更新)。
        3. 如果要使用改变后的状态,需要使用回调函数(setState 的第2个参数)。
        4. 如果新的状态,需要使用之前的状态参与计算,则使用函数的方式改变状态(setState 的第1个参数改为函数)

        1,异步更新状态

        this.setState() 改变状态后,会触发 render 函数执行,但不是立即执行。

        而状态的改变是异步还是同步,取决于执行 setState()的方法是否通过事件调用。如果是事件调用,则是异步的。

        状态的改变,才会触发 render 执行。

        下面的代码中,状态更新就是异步的。所以第1次点击时输出:0,render

        Python
        class MyClassComp extends Component {
            state = {
                num: 0,
            };
            handleClick = () => {
                this.setState({
                    num: this.state.num + 1,
                });
                console.log(this.state.num);
            };
            render() {
                console.log("render");
                return (
                    <>
                        {this.state.num}
                        this.handleClick}>1
                    
                );
            }
        }
        

        第2个参数

        setState() 的第2个参数是函数,可获取更改后的状态。该函数在 render 之后执行(状态改变,才能获取新的状态)。

        更改 handleClick 后,第1次点击输出:0,render,第2个参数 1

        Python
        handleClick = () => {
           this.setState({
                num: this.state.num + 1,
            }, () => {
                console.log('第2个参数',this.state.num);
            });
            console.log(this.state.num); 
        };
        

        2,多个 setState

        1,问题

        更改 handleClick 如下,第1次点击后输出:0,render。页面显示 1

        Python
        handleClick = () => {
           this.setState({
                num: this.state.num + 1,
            });
            this.setState({
                num: this.state.num + 1,
            });
            this.setState({
                num: this.state.num + 1,
            });
            console.log(this.state.num);
        };
        

        原因:因为状态更改是异步的,所以上面的代码相当于:

        Python
        handleClick = () => {
            this.setState({
                num: 0 + 1,
            });
            this.setState({
                num: 0 + 1,
            });
            this.setState({
                num: 0 + 1,
            });
            console.log(this.state.num);
        };
        

        2,配合第2个参数解决

        更改 handleClick 如下,第1次点击时输出:0,3次render,页面显示 3

        上面说了,第2个函数参数会在状态更新后执行,所以执行顺序:

        状态更新–> render --> 状态更新–> render --> 状态更新–> render

        Python
        handleClick = () => {
            this.setState({
                num: this.state.num + 1,
            }, () => {
                this.setState({
                    num: this.state.num + 1,
                }, () => {
                    this.setState({
                        num: this.state.num + 1,
                    })
                })
            });
            console.log(this.state.num);
        };
        

        3,第1个参数是函数

        使用场景:如果遇到某个事件中,需要同步调用多次setState,则可使用函数的方式得到最新状态。

        这种情况下,会将每个 setState 的函数参数放到一个队列中,按顺序执行。队列执行完毕后,再更新真正的 state,再执行 render(只执行了1次)。

        注意是对异步 setState 的处理。同步的更新并不会合并!render 函数会执行多次。

        为什么会合并?因为 React 对事件处理函数处理的思路时,元素的事件处理,可能会操作比较多的东西,如果不加限制,会影响性能。比如一个点击事件的逻辑中会触发多个自定义方法,每个方法中又会更改不同的状态。所以会将他们合并为一次修改,做成异步的,最终统一更新,执行一次 render 函数。

        而不在HTML元素的事件中,不会遇到复杂的处理,比如计时器中。

        在队列中的函数,可以获取上一个函数更新后的 state。换句话说,作为参数的 state 是可以被信任的(最新的)。

        更改 handleClick如下,第一次点击时输出:0,1,2,3,render,页面显示 3

        Python
        handleClick = () => {
            this.setState((curState) => {
                console.log(1);
                return {
                    num: curState.num + 1,
                };
            });
            this.setState((curState) => {
                console.log(2);
                return {
                    num: curState.num + 1,
                };
            });
            this.setState((curState) => {
                console.log(3);
                return {
                    num: curState.num + 1,
                };
            });
            console.log(this.state.num);
        };
        

        注意,第2个参数依旧会在 render 执行后执行。

        修改第1个 setState 如下,第一次点击的输出:0,1,2,3,render,x,

        Python
        this.setState((curState) => {
            console.log(1);
            return {
                num: curState.num + 1,
            };
        }, () => {
            console.log('x')
        });
        

        4,同步更新状态举例

        虽然是在点击事件中,但因为嵌套了计时器,所以可看做同步 setState。

        举例1:1s 后输出:render,1

        Python
        handleClick = () => {
            setTimeout(() => {
                this.setState({
                    num: this.state.num + 1,
                });
                console.log(this.state.num);
            }, 1000);
        };
        

        举例2:1s 后输出:3次render,3

        Python
        handleClick = () => {
            setTimeout(() => {
                this.setState({
                    num: this.state.num + 1,
                });
                this.setState({
                    num: this.state.num + 1,
                });
                this.setState({
                    num: this.state.num + 1,
                });
                console.log(this.state.num);
            }, 1000);
        };
        

        举例3,异步函数不会阻塞 render 执行,但异步函数之后的 setState 算做同步。

        Python
        const delay = (duration = 1000) => {
            return new Promise((resolve) => {
                setTimeout(() => {
                    resolve();
                }, duration);
            });
        };
        handleClick = async () => {
            this.setState({
                num: this.state.num + 1,
            });
            // 会同步执行 render
            await delay();
            // num 已经是修改后的
            this.setState({
                num: this.state.num + 1,
            });
            console.log(this.state.num);
        };
        

        第一次点击是的输出:render,(等待1s)render,2

        这里,第2个 render 比 2 先输出,就是因为异步函数之后,算做同步 setState 了。


        以上。

转载请注明来自码农世界,本文标题:《React@16.x(5)核心概念-setState》

百度分享代码,如果开启HTTPS请参考李洋个人博客
每一天,每一秒,你所做的决定都会改变你的人生!

发表评论

快捷回复:

评论列表 (暂无评论,78人围观)参与讨论

还没有评论,来说两句吧...

Top