概述
当页面中东西很多时,我们需要写大量的 JSX 到我们的 ReactDom.render()
中,这样会造成页面的冗长,所以需要组件来解决这个问题。
组件可以将 UI 拆分成独立的、可复用的部分,并且单独考虑每个部分。组件从概念上看就像是函数,它可以接收任意的输入值(称之为
props
),并返回一个需要在页面上展示的 React 元素
写一个组件
当有了组件之后,我们便有很多的可能性。demo2并在线运行,注意:组件的返回值只能有一个根元素。
这里我们使用三个组件,App
组件引用了 Box1
和 Box2
组件。
注意:
-
App
必须只有一个根元素。组件的返回值只能有一个根元素
props
传参
当调用自定义组件时,React 会将 JSX 属性作为对象(
props
) 传递给该组件。
注意:
- 组件名称必须以大写字母开头。
- 组件不能修改自己的
props
React 独到的点:标签就是函数,函数就是对象,标签的属性就是函数的参数。
class component
上述的 demo 在运行的时候发现只要更改一个,另外一个也会进行相应的更改,原因在于两个组件引用了相同的 text
,导致一个在改变 text
时,另一个也会改变.不可以通过 props
向两个组件传递 text
的值,因为组件不能修改自己的 props
。那么就需要创建一个作用域,将 text
放进作用域里面,那么将 text
放进函数中是否可以呢?
在实际的运行过程中,却发现更改不了 text
的值,原因在于当更改了 text
的值之后,再次 render
,造成了 text
的 init。点击前打开控制台,便可以看到,先 log
出 change
,之后再 log
出 init
,上述主要是 render
的时候 render
了整个 App
。那么如何做到即有自己的局部变量又可以进行局部 render
呢?这时便需要 class component
来解决了。
class component demo
注意:
- 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
可能会有问题,所以,要尽可能的使用componentDidUpdate
或setState()
回调,保证在更新被应用之后触发 - 除非
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()
第一个参数是带有signature
的updater
函数(prevState, props) => stateChange
prevState
是之前状态的引用,不能被改变。改变应该通过构建一个基于prevState
和props
输入的新的对象来进行。例如,假设想要通过props.step
在状态中增加一个值:this.setState((prevState, props) => { return {counter: prevState.counter + props.step}; });
updater
函数接收到的prevState
和props
保证都是最新的,updater
函数的输出是和state
的shallowly 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
可以对更新进行优化,它能将大批量的更新合并为一次更新,减少更新损耗 - 异步更新