React的setState原理与性能优化SCU

一、setState函数

react与vue对公共状态赋值有巨大的不同之处,vue对公共状态可以直接通过this来获取当前实例的公共状态,并且直接赋值即可。

而react并不能直接修改state的值来让界面发生更新,它是通过setState方法对公共状态进行修改。因为我们修改了state之后,希望React根据最新的State来重新渲染界面,但是这种方式的修改React并不知道数据发生了变化,React并没有实现类似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式来监听数据的变化。我们必须通过setState来告知React数据已经发生了变化。

1.基本用法

用法一:

import React, { Component } from 'react'

export class App extends Component {
  constructor() {
    super()
    this.state = {
      message: 'hello world'
    }
  }
  changContext() {
    // 1.基本用法
    this.setState({message: '你好,kk'})
  }
  render() {
    return (
      <div>
        <h2>{this.state.message}</h2>
        <button onClick={() => this.changContext()}>切换</button>
      </div>
    )
  }
}

export default App

用法二:传入一个回调函数

特点:

  • 可以在回调函数中编写新的state逻辑

  • 当前的回调函数可以将之前的state和props传递过来

import React, { Component } from 'react'

export class App extends Component {
  constructor() {
    super()
    this.state = {
      message: 'hello world'
    }
  }
  changContext() {
    // 2.传入一个回调函数
    this.setState((state, props) => {
      console.log(state, props); // 上一个state和props
      return {
        message: '你好,ss'
      }
    })
  }
  render() {
    return (
      <div>
        <h2>{this.state.message}</h2>
        <button onClick={() => this.changContext()}>切换</button>
      </div>
    )
  }
}

export default App

用法三:可以传递第二个参数(回调函数)

因为setState在react的事件处理中是一个异步调用,如果希望在数据更新之后(数据合并),获取到对应的结果进行逻辑处理,那么setState可以传递第二个参数(回调函数),在这个回调函数中调用this.state的值为修改后的值

import React, { Component } from 'react'

export class App extends Component {
  constructor() {
    super()
    this.state = {
      message: 'hello world'
    }
  }
  changContext() {
    // 3.setState在react的事件处理中是一个异步调用
    // 如果希望在数据更新之后(数据合并),获取到对应的结果进行逻辑处理
    // 那么setState可以传递第二个参数(回调函数)
    this.setState({message: '你好,kk'}, () => {
      console.log('修改后的meseage:', this.state.message); //修改后的meseage: 你好,kk
    })
    console.log('setState是异步调用,这时message还为旧值:', this.state.message); // setState是异步调用,这时message还为旧值: hello world
  }
  render() {
    return (
      <div>
        <h2>{this.state.message}</h2>
        <button onClick={() => this.changContext()}>切换</button>
      </div>
    )
  }
}

export default App

2.setState异步更新

setState设计为异步,可以显著的提升性能

如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步;

  • 如果每次调用 setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的;

  • 最好的办法应该是获取到多个更新,之后进行批量更新;

  • state和props不能保持一致性,会在开发中产生很多的问题;

二、React性能优化SCU

在开发中,我们只要是修改了App中的数据,所有的子组件都需要重新render,进行diff算法,性能必然是很低的。事实上,很多的组件没有必须要重新render,它们调用render应该有一个前提,就是依赖的数据(state、props)发生改变时,再调用自己的render方法;我们可以通过shouldComponentUpdate方法控制是否被调用。

1.shouldComponentUpdate生命周期函数

React给我们提供了一个生命周期方法 shouldComponentUpdate(很多时候,我们简称为SCU),这个方法接受参数,并且需要有返回值:

该方法有两个参数:

  • 参数一:nextProps 修改之后,最新的props属性

  • 参数二:nextState 修改之后,最新的state属性

该方法返回值是一个boolean类型:

  • 返回值为true,那么就需要调用render方法;

  • 返回值为false,那么久不需要调用render方法;

  • 默认返回的是true,也就是只要state发生改变,就会调用render方法

父组件:

import React, { Component } from 'react'
import Home from './Home'

export class App extends Component {
  constructor() {
    super()
    this.state = {
      message: 'hello world'
    }
  }
  changeContext() {
    this.setState({message: '你好,kk'})
  }
  shouldComponentUpdate(nextProps, nextState) {
    console.log(this.state.message, 'shouldComponentUpdate'); // setState是异步的,这个是state改变前的值
    if (this.state.message !== nextState) {
      return true
    } else {
      return false
    }
  }
  render() {
    console.log('App Render');
    console.log(this.state.message, 'render'); // setState是异步的,这个是state改变后的值
    return (
      <div>
        <span>{this.state.message}</span>
        <button onClick={() => this.changeContext()}>切换</button>
        <Home></Home>
        <HomePage></HomePage>
      </div>
    )
  }
}

export default App

子组件:

import React, { Component } from 'react'

export class Home extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    return false
  }
  render() {
    console.log('Home Render');
    return (
      <div>
        {/* <div>Home:{this.props.message}</div> */}
      </div>
    )
  }
}

export default Home

 2.PureComponent

如果所有的类,我们都需要手动来实现 shouldComponentUpdate,那么会给我们开发者增加非常多的工作量。我们可以将类继承自PureComponent

import React, { PureComponent } from 'react'

export class Home extends PureComponent {
  render() {
    console.log('Home Render');
    return (
      <div>
        {/* <div>Home:{this.props.message}</div> */}
      </div>
    )
  }
}

export default Home

如果函数组件也需要优化,那么我们可以通过memo函数来实现

import {memo} from 'react'

const HomePage = memo(function() {
  console.log('HomePage render');
  return (
    <div>HomePage</div>
  )
})

export default HomePage

3.不可变数据的力量

我们不能直接修改this.state里的对象或者数组,然后通过setState来修改值,这样在pureComponent中是无法更新this.state里的某下对象或数组值的,我们需要用浅拷贝赋值给一个变量,然后setState来将这个变量赋值给对应修改的属性。

addNewBook() {
    const newBook = { name: "Angular高级设计", price: 88, count: 1 }

    // 1.直接修改原有的state, 重新设置一遍
    // 在PureComponent是不能引入重新渲染(re-render)
    this.state.books.push(newBook)
    this.setState({ books: this.state.books })

    // 2.赋值一份books, 在新的books中修改, 设置新的books
    const books = [...this.state.books]
    books.push(newBook)

    this.setState({ books: books })
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值