当前位置: 首页 > article >正文

08react基础-react原理

setState()更新数据
  • setState()更新数据是异步的
  • 注意:使用该语法,后面的setState不要依赖前面setState的值
  • 多次调用setState,只会触发一次render
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component {
  state = {
    count: 1
  }

  handleClick = () => {
    // 此处,更新state
    // 注意:异步更新数据的!!!
    this.setState({
      count: this.state.count + 1
    })
    console.log('count:', this.state.count) // 1
    this.setState({
      count: this.state.count + 1 // 1 + 1
    })
    console.log('count:', this.state.count) // 1
  }
  render() {
    console.log('render')
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>+1</button>
      </div>
    )
  }
}
ReactDOM.render(<App />, document.getElementById('root'))
执行结果.png
setState()推荐语法
  • 基础语法:
this.setState((state, props) => {
return {
count: state.count + 1
  }
})
console.log(this.state.count) // 1
  • 完整代码:
import React from 'react'
import ReactDOM from 'react-dom'

/* 
  setState() 推荐语法
*/

class App extends React.Component {
  state = {
    count: 1
  }

  handleClick = () => {
  

    // 推荐语法:
    // 注意:这种语法也是异步更新state的!
    this.setState((state, props) => {
      return {
        count: state.count + 1 // 1 + 1
      }
    })
    this.setState((state, props) => {
      console.log('第二次调用:', state)
      return {
        count: state.count + 1
      }
    })
    console.log('count:', this.state.count) // 1
  }

  render() {
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>+1</button>
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
执行结果.png
setState()第二个参数
  • 场景:在状态更新(页面完成重新渲染)后立即执行某个操作
  • 语法:setState(update[,callback])
this.setState(
(state, props) => {},
() => {console.log('这个回调函数会在状态更新后立即执行')}
)

例子:

this.setState(
(state, props) => {},
() => {
document.title = '更新state后的标题:' + this.state.count
   }
)

具体代码:

import React from 'react'
import ReactDOM from 'react-dom'

/* 
  setState() callback
*/

class App extends React.Component {
  state = {
    count: 1
  }

  handleClick = () => {
    this.setState(
      (state, props) => {
        return {
          count: state.count + 1
        }
      },
      // 状态更新后并且重新渲染后,立即执行:
      () => {
        console.log('状态更新完成:', this.state.count) // 2
        console.log(document.getElementById('title').innerText)
        document.title = '更新后的count为:' + this.state.count
      }
    )
    console.log(this.state.count) // 1
  }

  render() {
    return (
      <div>
        <h1 id="title">计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>+1</button>
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
JSX语法的转化过程
image.png
  • JSX仅仅是createElement() 方法的语法糖(简化语法)
  • JSX语法被 @babel/preset-react 插件编译为createElement() 方法
  • React 元素: 是一个对象,用来描述你希望在屏幕上看到的内容


    image.png
组件更新机制
  • setState() 的两个作用

    • 修改state
    • 更新组件
  • 过程:父组件重新渲染时,也会重新渲染子组件,但只会渲染当前组件子树(当前组件以其所有子组件)


    image.png
import React from 'react'
import ReactDOM from 'react-dom'

/* 
  组件更新机制
*/

import './index.css'

// 根组件
class App extends React.Component {
  state = {
    color: '#369'
  }

  getColor() {
    return Math.floor(Math.random() * 256)
  }

  changeBG = () => {
    this.setState(() => {
      return {
        color: `rgb(${this.getColor()}, ${this.getColor()}, ${this.getColor()})`
      }
    })
  }

  render() {
    console.log('根组件')
    return (
      <div className="app" style={{ backgroundColor: this.state.color }}>
        <button onClick={this.changeBG}>根组件 - 切换颜色状态</button>
        <div className="app-wrapper">
          <Parent1 />
          <Parent2 />
        </div>
      </div>
    )
  }
}

// ------------------------左侧---------------------------

class Parent1 extends React.Component {
  state = {
    count: 0
  }

  handleClick = () => {
    this.setState(state => ({ count: state.count + 1 }))
  }
  render() {
    console.log('左侧父组件')
    return (
      <div className="parent">
        <h2>
          左侧 - 父组件1
          <button onClick={this.handleClick}>点我({this.state.count})</button>
        </h2>
        <div className="parent-wrapper">
          <Child1 />
          <Child2 />
        </div>
      </div>
    )
  }
}

class Child1 extends React.Component {
  render() {
    console.log('左侧子组件 - 1')
    return <div className="child">子组件1-1</div>
  }
}
class Child2 extends React.Component {
  render() {
    console.log('左侧子组件 - 2')
    return <div className="child">子组件1-2</div>
  }
}

// ------------------------右侧---------------------------

class Parent2 extends React.Component {
  state = {
    count: 0
  }

  handleClick = () => {
    this.setState(state => ({ count: state.count + 1 }))
  }

  render() {
    console.log('右侧父组件')
    return (
      <div className="parent">
        <h2>
          右侧 - 父组件2
          <button onClick={this.handleClick}>点我({this.state.count})</button>
        </h2>
        <div className="parent-wrapper">
          <Child3 />
          <Child4 />
        </div>
      </div>
    )
  }
}

class Child3 extends React.Component {
  render() {
    console.log('右侧子组件 - 1')
    return <div className="child">子组件2-1</div>
  }
}
class Child4 extends React.Component {
  render() {
    console.log('右侧子组件 - 2')
    return <div className="child">子组件2-2 </div>
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
更新子组件时.png

更新根组件时..png
组件性能优化
  • 1.减轻state
  • 减轻state:只存储跟组件渲染相关的数据(比如:count/ 列表数据 /loading等)
  • 注意:不用做渲染的数据不要放在state中
  • 对于这种需要在多个方法中用到的数据,应该放到this中
class Hello extends Component {
componentDidMount() {
// timerId存储到this中,而不是state中
this.timerId = setInterval(() => {}, 2000)
  }
componentWillUnmount() {
clearInterval(this.timerId)
   }
render() { … }
}
  • 2.避免不必要的重新渲染
  • 组件更新机制:父组件更新会引起子组件也被更新,这种思路很清晰
  • 问题:子组件没有任何变化时也会重新渲染
  • 如果避免不必要的重新渲染?
  • 解决方式:使用钩子函数shouldComponentUpdate(nextProps, nextState)
    • 在这个函数中,nextPropsnextState是最新的状态以及属性
  • 作用:这个函数有返回值,如果返回true,代表需要重新渲染,如果返回false,代表不需要重新渲染
  • 触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate => render)
class Hello extends Component {
shouldComponentUpdate() {
// 根据条件,决定是否重新渲染组件
return false
}
render() {…}
}

随机数案例 如果随机获取的数值和之上一次一样不更新组件否则就更新

import React from 'react'
import ReactDOM from 'react-dom'

/* 
  组件性能优化:
*/

// 生成随机数
class App extends React.Component {
  state = {
    number: 0
  }

  handleClick = () => {
    this.setState(() => {
      return {
        number: 1
      }
    })
  }

  // 因为两次生成的随机数可能相同,如果相同,此时,不需要重新渲染
  shouldComponentUpdate(nextProps, nextState) {
    console.log('最新状态:', nextState, ', 当前状态:', this.state)

    return nextState.number !== this.state.number

    // if (nextState.number !== this.state.number) {
    //   return true
    // }
    // return false

    // if (nextState.number === this.state.number) {
    //   return false
    // }
    // return true
  }

  render() {
    console.log('render')
    return (
      <div>
        <h1>随机数:{this.state.number}</h1>
        <button onClick={this.handleClick}>重新生成</button>
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
  • nextprops:
import React from 'react'
import ReactDOM from 'react-dom'

/* 
  组件性能优化:
*/

// 生成随机数
class App extends React.Component {
  state = {
    number: 0
  }

  handleClick = () => {
    this.setState(() => {
      return {
        number: Math.floor(Math.random() * 3)
      }
    })
  }

  // 因为两次生成的随机数可能相同,如果相同,此时,不需要重新渲染
  // shouldComponentUpdate(nextProps, nextState) {
  //   console.log('最新状态:', nextState, ', 当前状态:', this.state)
  //   return nextState.number !== this.state.number
  // }

  render() {
    // console.log('render')
    return (
      <div>
        <NumberBox number={this.state.number} />
        <button onClick={this.handleClick}>重新生成</button>
      </div>
    )
  }
}

class NumberBox extends React.Component {
  shouldComponentUpdate(nextProps) {
    console.log('最新props:', nextProps, ', 当前props:', this.props)
    // 如果前后两次的number值相同,就返回false,不更新组件
    return nextProps.number !== this.props.number

    // if (nextProps.number === this.props.number) {
    //   return false
    // }
    // return true
  }
  render() {
    console.log('子组件中的render')
    return <h1>随机数:{this.props.number}</h1>
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
纯组件
作用以及使用
  • 纯组件: PureComponentReact.Component 功能相似
  • 区别: PureComponent 内部自动实现了 shouldComponentUpdate钩子,不需要手动比较
  • 原理:纯组件内部通过分别比对前后两次 props和state的值,来决定是否重新渲染组件
class Hello extends React.PureComponent {
render() {
return (
<div>纯组件</div>
     )
  }
}
  • 实现原理
  • 说明:纯组件内部的对比是 shallow compare(浅层对比)
  • 对于值类型来说:比较两个值是否相同
const obj = { number: 0 }
const newObj = obj
newObj.number = 2
console.log(newObj === obj) // true
  • 引用类型:只比对对象的引用地址是否相同
state = { obj: { number: 0 } }
// 错误做法
state.obj.number = 2
setState({ obj: state.obj })
// PureComponent内部比较:
最新的state.obj === 上一次的state.obj // true,不重新渲染组件
  • 注意:state 或 props 中属性值为引用类型时,应该创建新数据,不要直接修改原数据
// 正确!创建新数据
const newObj = {...state.obj, number: 2}
setState({ obj: newObj })
// 正确!创建新数据
// 不要用数组的push / unshift 等直接修改当前数组的的方法
// 而应该用 concat 或 slice 等这些返回新数组的方法
this.setState({
list: [...this.state.list, {新数据}]
})

案例:

import React from 'react'
import ReactDOM from 'react-dom'

/* 
  组件性能优化:
*/

// 引用类型:
const obj = { number: 0 }
const newObj = obj
newObj.number = 2
console.log(newObj === obj) // true

// 生成随机数
class App extends React.PureComponent {
  state = {
    obj: {
      number: 0
    }
  }

  handleClick = () => {
    // 正确做法:创建新对象
    const newObj = { ...this.state.obj, number: Math.floor(Math.random() * 3) }
    this.setState(() => {
      return {
        obj: newObj
      }
    })

    // 错误演示:直接修改原始对象中属性的值
    /* const newObj = this.state.obj
    newObj.number = Math.floor(Math.random() * 3)

    this.setState(() => {
      return {
        obj: newObj
      }
    }) */
  }

  render() {
    console.log('父组件重新render')
    return (
      <div>
        <h1>随机数:{this.state.obj.number}</h1>
        <button onClick={this.handleClick}>重新生成</button>
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
虚拟 DOM 和 Diff 算法
  • React更新视图的思想是:只要state变化就重新渲染视图
  • 特点:思路非常清晰
  • 问题:组件中只有一个DOM元素需要更新时,也得把整个组件的内容重新渲染吗? 不是这样的
  • 理想状态:部分更新,只更新变化的地方
  • React运用的核心点就是 虚拟DOM 配合 Diff 算法
虚拟DOM

虚拟 DOM:本质上就是一个 JS 对象,用来描述你希望在屏幕上看到的内容(UI)。


image.png
Diff算法

执行过程

  • 初次渲染时,React会根据初始化的state(model),创建一个虚拟DOM对象(树)
  • 根据虚拟DOM生成真正的DOM,渲染到页面
  • 当数据变化后(setState()),会重新根据新的数据,创建新的虚拟DOM对象(树)
  • 与上一次得到的虚拟DOM对象,使用Diff算法比对(找不同),得到需要更新的内容
  • 最终,React只将变化的内容更新(patch)到DOM中,重新渲染到页面
image.png

代码演示:

import React from 'react'
import ReactDOM from 'react-dom'

/* 
  虚拟DOM 和 Diff算法
*/

// 生成随机数
class App extends React.PureComponent {
  state = {
    number: 0
  }

  handleClick = () => {
    this.setState(() => {
      return {
        number: Math.floor(Math.random() * 2)
      }
    })
  }

  // render方法调用并不意味着浏览器中的重新渲染!!!
  // render方法调用仅仅说明要进行diff
  render() {
    const el = (
      <div>
        <h1>随机数:</h1>
        <p>{this.state.number}</p>
        <button onClick={this.handleClick}>重新生成</button>
      </div>
    )
    console.log(el)

    return el
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
image.png
最后编辑于:2025-02-24 21:39:42


喜欢的朋友记得点赞、收藏、关注哦!!!


http://www.kler.cn/a/576555.html

相关文章:

  • 【K8S系列】深入探究Kubernetes中查看日志的方法
  • 中原银行:从“小机+传统数据库”升级为“OceanBase+通用服务器”,30 +系统成功上线|OceanBase DB大咖说(十五)
  • 【Linux】基础IO_文件系统基础
  • 交大智邦后端Java笔试题
  • Linux驱动学习笔记之I2C通信(观b站讯为电子有感)
  • HOW - React 如何在在浏览器绘制之前同步执行 - useLayoutEffect
  • 2025-03-07 electron无法打包的小问题
  • 20242817李臻《Linux⾼级编程实践》第二周
  • WordPress报502错误问题解决-php-fpm-84.service loaded failed failed LSB: starts php-fpm
  • 为AI聊天工具添加一个知识系统 之133 详细设计之74通用编程语言 之4 架构及其核心
  • 【SegRNN 源码理解】self.revIN可逆实例标准化
  • Elasticsearch:“Your trial license is expired”
  • 乐鑫打造全球首款 PSA Certified Level 2 RISC-V 芯片
  • Qt 实现绘图板(支持橡皮擦与 Ctrl+Z 撤销功能)[特殊字符]
  • React Native 0.76 升级后 APK 体积增大的原因及优化方案
  • linux的文件系统及文件类型
  • 护照阅读器在旅游景区流程中的应用
  • 深度学习网格搜索实战
  • GPO 配置的 4 种常见安全错误及安全优化策略
  • 机器学习(李宏毅)——Life-Long Learning