快来加入我们吧!《小河山菜鸟》为前端开发者提供技术资料和系列基础文章。为了更好的用户体验,请移步我们的官网小河山菜鸟(https://xhs-rookies.com/)学习,及时获取最新文章。“代码裁缝”,如果您对我们的文章感兴趣,或者想提点建议,请关注“小河山菜鸟”微信公众号,与我们取得联系,您也可以在微信上观看我们的文章。每一个建议或认可,都是对我们莫大的鼓励!在前言的这一部分,我们将介绍React中的setState,希望能帮助大家真正理解setState。本文将向大家介绍以下内容:setState的使用方法不能直接修改StatesetState()setState可能是异步更新setState的组合setState的使用方法在介绍setState之前,先看一个setState的案例,了解一下它是如何实现的用过的。举个例子,当点击按钮改变文字时,修改界面前面显示的内容:(道具)this.state={message:'HelloReact',}}render(){return(
{this.state.message}
this.changeMes??sage()}>ChangeMes??sage)}changeMes??sage(){this.setState({message:'Helloxhs,yourmessageischanged.',})}}状态初始化在类中constructor如果要改变状态,可以使用setState函数。这个函数的主要作用是传入一个对象作为参数,这个对象就是你要修改的值。上面代码中,当你点击ChangeMes??sage时,会调用setState函数,setState会调用render函数,页面会重新渲染。点击按钮后,重新渲染的效果:不能直接修改State把上面的changeMes??sage方法改成下面的。changeMes??sage(){this.state.message="helloxhs,yourmessageischanged.";}点击ChangeMes??sage后页面没有变化,但是你会发现state中的message变了,但是页面不会被改变重新渲染。构造函数是我们唯一可以给this.state赋值的地方,要想重新渲染页面,只能通过setState函数修改。因此,我们通过调用setState修改数据:调用setState时,会重新执行render函数,根据最新的State创建ReactElement对象,然后根据最新的ReactElement对象修改DOM;changeMes??sage(){this.setState({message:"Helloxhs,yourmessageischanged."})}setState()setState(updater,[callback])setState()将组件状态的更改排队并通知React它需要以更新后的状态重新启动呈现此组件及其子组件。这是更新UI以响应事件处理程序和处理服务器数据的主要方式。将setState()视为请求而不是立即更新组件的命令。为了获得更好的感知性能,React延迟调用它,然后一次更新多个组件。React不保证状态更改会立即生效。setState()并不总是立即更新组件。它分批推迟更新。这使得在调用setState()之后立即读取this.state成为一种危险。为了消除隐患,请使用componentDidUpdate或者setState的回调函数(setState(updater,callback)),两者都可以保证应用更新后触发。参数1可以有两种形式1.接受对象类型setState(stateChange[,callback])stateChange会将传入的对象浅合并到新的状态中,例如letcount+1this.setState({count:this.state.count+1})这种形式的setState()也是异步的,并且在同一个周期中批处理多个setStates。例如count+1在同一个循环中重复多次,就相当于:Object.assign(previousState,//previousstate{count:state.count+1},{count:state.count+1},...)后面调用的setState()会在同一个循环中覆盖前面调用的setState的值,所以item的数量只会增加一次。如果后续状态依赖于当前状态,我们建议改用updater函数的形式:this.setState((state)=>{return{count:state.count+1}})2.接受函数参数我们是在初始状态并且没有计数。但是现在我们有一个需求:就是在state里面加一个count,把object作为第一个参数,就会发现这样的问题。changeCount(){this.setState({count:0})//=>this.state.count未定义this.setState({count:this.state.count+1})//=>undefined+1=NaNthis.setState({count:this.state.count+2})//=>NaN+2=NaN}上面代码的结果不符合我们的预期。我们希望count操作的结果是3,但是最后得到的是NaN。但是,此类后续操作依赖于先前setState的结果并不少见。这就很自然地引出了第二种使用setState的方式,它可以接受一个函数作为参数。React.js会将之前setState的结果传入这个函数,你可以使用这个结果进行计算和运算,然后返回一个对象作为更新状态的对象:changeCount(){this.setState((prevState)=>{return{count:0}})this.setState((prevState)=>{return{count:prevState.count+1}//最后一个setState的返回是count为0,这里需要执行+1,所以当前return为1})this.setState((prevState)=>{return{count:prevState.count+2}//之前setState的return是count为1,这里需要toexecute+2,sothecurrentreturnis3})//最终结果this.state.count为3}这样就可以达到上述使用上次setState的结果执行操作的效果。第二个参数是回调函数。setState()的第二个参数是一个可选的回调函数,在setState完成合并并重新渲染组件后执行。一般来说,我们建议改用componentDidUpdate()。让我们在示例中的changeMes??sage函数中添加两个打印方法。changeMes??sage(){this.setState({message:"Helloxhs,yourmessageischanged."},()=>{console.log('回调结果:',this.state.message);})console.log('无回调结果:',this.state.message);}我们来看一下结果。从图中我们可以看到,当setState在外面的时候,并不能马上得到我们想要的结果。回调返回的是修改后的结果。正因为我们没有立即得到我们想要的结果,所以这就是我们接下来要说的。setState可能是一个异步更新。这样我们就可以知道setState的第二个参数的作用,就是保证获取到state修改后的结果。也就是重新渲染后执行的是什么。setState可能会异步更新我们看下面的代码:最终打印的结果是HelloReact;可以看出setState是一个异步操作,执行完setStatechangeMes??sage()后我们并不能立即得到最新的state结果这个.state.message);//HelloReact}为什么setState要设计成异步的?SetState被设计为异步的。其实之前在GitHub上也有很多讨论;React核心成员(Redux的作者)DanAbramov也有相应的回复,大家可以参考一下;让我们简单总结一下他的回答:setState被设计成异步的,可以显着提高性能;如果每次调用setState都进行update,意味着会频繁调用render函数,界面会重新渲染,效率很低;注意:最好的方式应该是获取多个更新,然后进行批量更新;如果state是同步更新的,但是render函数还没有执行,那么state和props就不能保持同步;注意:state和props不能保持一致,会导致开发中出现很多问题;获取到update后setState的第二个参数是一个回调函数,会在update之后执行;changeMes??sage(){this.setState({message:"Helloxhs,yourmessageischanged."},()=>{console.log(this.state.message);//Helloxhs,你的消息已更改。});}当然我们也可以使用生命周期函数:componentDidUpdate(prevProps,provState,snapshot){console.log(this.state.message);}setState一定要异步?疑惑:setState一定要异步更新吗?验证1:setTimeout中的更新:changeText(){setTimeout(()=>{this.setState({message:"你好,小河山"});console.log(this.state.message);//你好,小河山},0);}验证2:原生DOM事件:componentDidMount(){constbtnEl=document.getElementById("btn");btnEl.addEventListener('click',()=>{this.setState({message:"你好,小河山"});console.log(this.state.message);//你好,小河山})}其实是分的分为两种情况:在组件生命周期或React合成事件中,setState是异步的;在setTimeout或原生dom事件中,setState是同步的;setState的合并数据的合并当你调用setState()时,React会将你提供的对象合并到当前状态中,并为状态this定义一些数据。state={name:'xhs',message:'HelloReact',count:0,}通过setState修改state中的message,不会影响namechangeMes??sage(){this.setState({message:"Helloxhs,你的消息被改变了。”});}多个setStates将被合并。比如我们还有一个count属性,默认为0,记录当前的个数:执行下面的操作后,最后的答案是大于1stateIncrement(){this.setState({count:this.state.计数+1});this.setState({count:this.state.count+1});this.setState({count:this.state.count+1});}怎么才能让count最后变成3呢?increment(){this.setState((state,props)=>{return{count:state.count+1}})this.setState((state,props)=>{return{count:state.count+1}})this.setState((state,props)=>{return{count:state.count+1}})}上面的代码用到了,setState接收一个函数作为第一个参数来解决这个问题,可以得到我们期望的结果非常好。下一节预览本节。在这一节中,我们了解了React中SetState的奥秘。下一章我们将继续学习React中受控组件和非受控组件的内容。敬请关注!