简单总结下与setState有关的问题

1、为什么需要setState?

        在react中并没有类似vue的数据劫持,即直接修改数据,页面跟着更新。所以我们需要显式的调用一个方法去修改数据,并且更新页面,这个方法就是setState。

2、其他的用法

        我们在使用setState时,都是只传入需要修改的数据,对于其他不需要修改的数据,react是进行对象的合并,即用Object.assign。setState除了直接传入对象,还有其他的用法

(1)传入回调

        在setState中传入回调函数,函数返回需要修改的数据,在这个回调中能拿到上一次setState修改后的state。

        见下面代码,三个setState会依次打印上一次setState后的state结果。【但此时的setState不是同步的】

//    23 'set之后'
//    23
//    24
//    25
  state = {
    age: 23
  }
  changeAge = () => {
    this.setState((state) => {
      console.log(state.age)
      return { age: state.age + 1 }
    })
    this.setState((state) => {
      console.log(state.age)
      return { age: state.age + 1 }
    })
    this.setState((state) => {
      console.log(state.age)
      return { age: state.age + 1 }
    })
    console.log(this.state.age,'set之后')
  }

        【使用useState的set方法也能传入回调,在回调中能拿到上一次更新的结果。即: setCount(preCount=>preCount+1)

(2)第二个参数是回调

        其实我们知道在react18中setState都是异步的,但是有时我们希望拿到更改state后最新的dom,类似vue中的$nextTick。这时我们就能在setState中传入第二个参数,这是一个回调,dom更新完毕后,react会去调用这个回调。

  changeAge = () => {
    this.setState({ age: 244 }, () => {
      // 此时的dom是最新的
      console.log(this.refs.myP)
    })

(3) flushSync中的setState变为同步

        从react-dom中引入flushSync,给这个函数传入回调,在回调中setState,在flushSync的外部打印的state就是最新的。【但是官网说慎用,不推荐】

import {flushSync} from 'react-dom'    //注意是引入react-dom
flushSync(()=>{
	this.setState({msg:'aa'})
})
console.log(this.state.msg) //'aa'

3、setState同步还是异步?

        在react18中setState都是异步的

react18之前组件生命周期或者react合成事件中setState是异步的。

setTImeout、promise或者原生dom事件中是同步的

        如果同时连续调用setState修改值,react会将这些调用批量处理,就是将其合成为一个更新(这就与多次setTimeout改一个变量的值不同了)。看下面的示例代码:

//count:0
this.setState({
	count:this.state.count + 3
})
this.setState({
	count:this.state.count + 2
})
this.setState({
	count:this.state.count + 6
})
//批处理,进行合并,最后结果是6

         所以推荐在使用setState时使用传入回调的方式。

为什么搞成成异步的?

        总结下来就是能提高性能,同步的话频繁setState,会频繁render,其实不需要频繁render, 所以最好是最后一次更新就行(一次render)。

4、什么是数据不可变力量

        就是告诉我们不要直接去修改state,而是用他提供的setState方法修改state。

        可以看下面的例子来理解。

state = {
	books:['1']
}

//修改
this.state.books.push('3')
this.setState({
  books:this.state.books  
})

        在类组件继承PureComponent的情况下,执行上面代码,页面不变化。不刷新的原因很明显,PureComponent会对state进行浅层比较,而新旧books指向同一块地址,就会判定books的值没有发生变化,所以页面不更新。

        所以我们在修改引用类型的数据应该浅拷贝变量,再修改拷贝好的数据,最后 setState。

5、传入同一个值-优化

         当我们设置同一个值,最好页面不要进行更新,下面分析两种组件下的具体情况。

//count:0

this.setState({
     count: 0
})

        setState

        在类组件中,用setState设置同一个值,页面也是会更新的,要想页面不更新,我们需要让这个类组件继承PureComponent,而不是之前的Component。

class App extends React.PureComponent {
    //.....
}

         其实我们还需要对props进行浅层比较,而PureComponent有做这些操作。

        PureComponent的原理

        在shouldCompoentUpdate这个生命周期中对state和props进行浅层比较,通过返回布尔值来决定是否更新。比如下面的代码:

//执行shouldComponentUpdate时state还没有修改
shouldComponentUpdate(nextProps,nextState){		//新的值
	if(this.state.age !== nextState.age){
		return true
	}
    return false
}

        useState的set方法 

         在函数组件中,本身useState的set方法会进行浅层比较,即传入相同的值也不会更新页面。但是useState并不会去对props进行浅层比较,为了更好的优化,通常我们还需要使用memo这个高阶组件(即用memo去对props进行比较)。

const Home = memo(() => {
    //....
})

        memo除了第一个参数传入函数组件外,第二个参数还能传入自定义的比较函数,这个比较函数接收两个参数:前一个 props 和后一个 props,返回一个布尔值来决定是否重新渲染。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值