React 初探(二) - 组件、DOM diff 、setState

概述

当页面中东西很多时,我们需要写大量的 JSX 到我们的 ReactDom.render() 中,这样会造成页面的冗长,所以需要组件来解决这个问题。

组件可以将 UI 拆分成独立的、可复用的部分,并且单独考虑每个部分。组件从概念上看就像是函数,它可以接收任意的输入值(称之为 props),并返回一个需要在页面上展示的 React 元素

写一个组件

在线运行

function component

当有了组件之后,我们便有很多的可能性。demo2并在线运行,注意:组件的返回值只能有一个根元素。

function component 嵌套

这里我们使用三个组件,App 组件引用了 Box1Box2 组件。

注意:

  • App 必须只有一个根元素。组件的返回值只能有一个根元素

props 传参

demo并在线运行

props 传参

当调用自定义组件时,React 会将 JSX 属性作为对象(props) 传递给该组件。

注意:

  • 组件名称必须以大写字母开头。
  • 组件不能修改自己的 props

React 独到的点:标签就是函数,函数就是对象,标签的属性就是函数的参数。

class component

上述的 demo 在运行的时候发现只要更改一个,另外一个也会进行相应的更改,原因在于两个组件引用了相同的 text,导致一个在改变 text 时,另一个也会改变.不可以通过 props 向两个组件传递 text 的值,因为组件不能修改自己的 props 。那么就需要创建一个作用域,将 text 放进作用域里面,那么将 text 放进函数中是否可以呢?

function component

在实际的运行过程中,却发现更改不了 text 的值,原因在于当更改了 text 的值之后,再次 render,造成了 text 的 init。点击前打开控制台,便可以看到,先 logchange,之后再 loginit,上述主要是 render 的时候 render 了整个 App那么如何做到即有自己的局部变量又可以进行局部 render 呢?这时便需要 class component 来解决了

class component demo

在线运行

class component

注意:

  • class component 必须继承 React.Component
  • constructor 里面必须调用 super()
  • 必须在 constructor 里面初始化 state
  • 必须有 render 函数,并且必须 return 一个标签,并且这个标签只能有一个根元素
  • 无论是 function || class component 都不能更改 props
  • 如果要使用变量需要 this.props.xxx || this.state.xxx
  • 调用函数时可以使用 bind,也可以使用箭头函数,即 onClick = {() => this.onButtonClick}

知道了如何进行 class component 的写法,就可以写出上面的例子了,demo代码并在线运行,当使用 class component 的时候,可以有自己的局部变量,并可以进行局部更新

DOM diff

上述的例子中,当点击 change 按钮时,会调用绑定的 onButtonClick 函数,函数中会进行 setState,得到新的 state,新的 state 更新之后会调用 render 函数,render 函数只会更新 span,并且仅仅会更新更改的地方,其他的地方是不会进行更改的。每次 render 都会进行对比,和上一次的结果进行比较,只更改 state 变化的部分,这个找到哪里变化的过程,叫做DOM diff,找两次 render 结果不同之处的过程即是DOM diff

API

setState()

setState(updater[, callback])

关于 setState 需要知道以下几点:

  • setState 是用于更新用户界面以响应事件处理程序( event handle ) 和服务器响应的主要方式 (This is the primary method you use to update the user interface in response to event handlers and server responses.)
  • setState() 视为更新组件的请求而不是立即执行的命令,React 可能会 delay 它,React 不能保证状态更改会立即被应用
  • 因为 setState() 不是立刻更新组件,所以在调用 setState() 之后立即读取 this.state 可能会有问题,所以,要尽可能的使用 componentDidUpdatesetState() 回调,保证在更新被应用之后触发
  • 除非 shouldComponentUpdate() 返回 false,否则 setState() 将始终导致重新渲染,If mutable objects are being used and conditional rendering logic cannot be implemented in shouldComponentUpdate(), calling setState() only when the new state differs from the previous state will avoid unnecessary re-renders.
  • 如果需要基于之前的状态设置状态,需要使 setState() 第一个参数是带有 signatureupdater 函数
    (prevState, props) => stateChange
    
    prevState 是之前状态的引用,不能被改变。改变应该通过构建一个基于 prevStateprops 输入的新的对象来进行。例如,假设想要通过 props.step 在状态中增加一个值:
    this.setState((prevState, props) => {
      return {counter: prevState.counter + props.step};
    });
    
    updater 函数接收到的 prevStateprops 保证都是最新的,updater 函数的输出是和 stateshallowly merged
  • setState() 第二个函数时可选的回调函数,在 setState 完成并且组件重新被重新渲染之后被执行一次,通常将 componentDidupdate() 用于此类逻辑
  • setState() 第一个参数也可以也可以使用一个对象,而不是函数
    setState(stateChange[, callback])
    
    这将执行 shallowly merged 到新状态中:
    this.setState({quantity: 2});
    
    这一形式的 setState() 也是异步的,同一周期中多个调用将被合并到一起。例如,如果在相同的周期中尝试多次增加一件物品的数量,其等价于:
    Object.assign(
      previousState,
      {quantity: state.quantity + 1},
      {quantity: state.quantity + 1},
      ...
    )
    
    之后的调用将会重写之前调用的值,因此数量仅会被加一。如果之后的状态依赖于之前的状态,推荐使用 updater 函数形式
    this.setState((state) => {
      return {quantity: state.quantity + 1};
    });
    

优缺点

  • setState 可以对更新进行优化,它能将大批量的更新合并为一次更新,减少更新损耗
  • 异步更新

你可能感兴趣的:(React 初探(二) - 组件、DOM diff 、setState)