学习内容:
- 英语小课堂
- 创建方式
- props和state
- 生命周期
一、英语小课堂
- derived 导出的、衍生的、派生的
- super class 超类、父类
- mount 挂载
- render 渲染
- property 属性
- state 状态
二、创建方式
两种创建方式
1. ES5
import React from 'react'
const A = React.createClass({
render(){
return (<div>hi</div>)
}
})
export default A
2. ES6
import React from 'react'
class B extends React.Component{
constructor(props){
super(props)
}
render(){
return (<div>hi</div>)
}
}
export default B
3.两种方法比较
ES5已经过时,基本不用,如果IE8的浏览器要用,webpack+Babel转换ES6为ES5
三、props外部数据
1.对外部数据的初始化
- 要传出的外部数据被包装成为一个对象
- {name:'yuyuan',onClick:……,childrean:'hi'}
- 此处的onClick是一个回调
class Parent extends React.Component{
constructor(props){
super(props)
this.state = {name:'yuyuan'}
}
onClick = ()=>{}
render(){
return (
<B name={this.state.name //从这里开始就是包装了
onClick = {this.onClick}
}>hi</B>
)
}
}
2.外部数据的引入和读取
- 通过传参的形式引入,这样this.props就是外部数据对象的地址了
- 通过this.props.xxx读取
class B extends React.Component{
constructor(props){
super(props) //引入
}
render(){
return(
<div onClick={this.props.onClick}>
{this.props.name}
{this.props.children} //读取
</div>
)
}
}
3.一些对props的SB写入操作
- 改props的值(改一个地址) this.props={另一个对象}
- 改props的属性 this.props.xxx = 'hi' 应该由传入原始数据的一方来修改数据,没有理由进行外部的更新
4.关于props的钩子——componentWillReceiveProps
作用:当组件接受新的props的时候,会触发此钩子,得到新旧的props值
但是这个钩子已经过气被弃用,学习仅仅是防止看不懂之前的代码
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
constructor(props){
super(props)
this.state = {n:0}
}
onClick = ()=>{this.setState({n:this.state.n+1})}
render(){
return (
<div>n:{this.state.n} -App <button onClick={this.onClick}>+1</button>
<B name={this.state.n} onClick = {this.onClick}/>
</div>
)
}
}
class B extends React.Component{
componentWillReceiveProps(newProps,nextContent){ //钩子
console.log('旧的props',this.props)
console.log('props变了')
console.log('新的props',newProps)
}
constructor(props){
super(props)
}
render(){
return (
<div>name:{this.props.name} - B <button onClick={this.props.onClick}>+1</button></div>
)
}
]
ReactDOM.render(<App />,document.getElementById('root'))
5.props的作用
- 接受外部数据
- 只能读不能写,外部数据由父组件传递
- 接受外部函数
- 在恰当时机调用该函数,该函数一般是父组件的函数
四、state内部数据
1. state的初始化
this.state = {user:{name:'yuyuan',age:18},n:0}
2. state的读取
{this.state.n}
{this.user.name}
3. state的写入
- setState不会立刻改变this.setState,会在当前代码运行完后,再去更新this.state,从而触发UI更新
- fn会在写入成功后执行 fn使用示例
A.对象式写入
this.setState(newState,fn)
B.函数式写入
this.setState((state,props)=>(newState),fn)
五、生命周期 Lifecycle
1.DOM的生命周期
- div的create/constructor过程
let div = document.createElement('div')
- div初始化state
div.textContent = 'hi'
- div的mount过程
document.body.appendChild(div)
- div的update过程
div.textContent = 'Hi'
- div的unmount过程
div.remove()
2.React的生命周期
React生命周期列表
重要列表
- constructor() 初始化state
- shouldComponentUpdate() return false阻止更新
- render() 创建虚拟DOM
- componentDidUpdate() 组件已更新
- componentDidMount() 组件已出现在页面中
- componentWillUnmount() 组件将死
不重要
- static getDerivedStateFromProps()
- getSnapshotBeforeUpdate()
- static getDerivedStateFromError()
- componentDidCatch()
constructor()
用途:
- 用来初始化state
- 用来初始化props
- 用来写bind this
constructor(){
……
this.onClick.bind(this)
}
//上面的语法等价于新语法
onClick =()=>{}
constructor(){……}
- 没有state要写的情况下,constructor可以不写
shouldComponentUpdate()
1.页面刷新原理解释
state ---render---> DOM ----> UI
graph TD
oldState.Vs.newState --> 比较两个state是否相同 --> 不同就render数据 --> 创建虚拟DOM --> diff比较两个虚拟DOM是否相同 --> 不同就刷新UI界面
2.state不同DOM相同的实例
因为React中对数据进行修改不是在原来的数据上进行的。即使值相同也会出现对象的地址不同导致比较时,React认为这两个数据就是不同的,但是此时render后创建的虚拟DOM是一样的,UI就不会刷新。
import React from 'react'
import ReactDOM from 'react-dom'
class App extends React.Component{
constructor(props){
super(props)
this.state = {n:0}
}
onClick = ()=>{
this.setState(state=>({n:state.n+1}))
this.setState(state=>({n:state.n-1}))
}
render(){
console.log('render了!')
return (
<div>App<button onClick={this.onClick}>+1</button></div>
)
}
}
ReactDOM.render(<App/>,document.getElmentById('root'))
3.shouldComponentUpdate()手动选择是否更新组件
在上一个案例的render前写上
shouldComponentUpdate(newProps,newState){
if(newState.n === this.state.n){return false}else{ return true}
}
return false 表示不刷新组件,return true表示刷新组件
4.shouldComponentUpdate的替代PureComponent
写原来的阻止更新,只能自己手动一个一个去比较,太麻烦了,React内置了新的方法帮你去一个一个地判断。
PureComponent工作原理:
对每一个state和props的key以及value进行比较,如果这些是相同的,就不会去更新组件,创建新的虚拟DOM了。
class App extends React.PureComponent{
……
}
5.阻止组件更新的过程
shouldComponentUpdate和PureComponent比较了state的key和value,相同就直接没有新的虚拟DOM的创建,不会刷新UI了。
6.面试题:shouldComponentUpdate有什么用?
它允许我们手动判断是否要更新组件,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新。
render()
1. render()创建虚拟DOM元素用于展示视图
如何验证render()创建的是虚拟DOM呢? 查看render的是个什么东西
2.render中只能有一个根元素
render(){
return (
<div>hi</div>
<div>Hi</div>
)
}
这样会报错,因为有两个根元素
3.<><React.Fragment>占位根元素
用来解决一个根元素的问题,将<React.Fragment>或者</>作为占位的根元素
render(){
return (
<React.Fragment>
<div>hi</div>
<div>Hi</div>
</React.Fragment>
)
}
写这么多太长了,react给你简化
render(){
return (
<>
<div>hi</div>
<div>Hi</div>
</>
)
}
4.render()中的if-else
render()中可以直接写if-else,当然也可以用{}来写?:表达式
render(){
let message = ''
if(this.state.n%2===0){message=(<div>偶数</div>)}
else{message=(<div>奇数</div>)}
return (
<div>{this.state.n}<button onClick={this.onClick}>+1</button>{message}</div>
)
}
render(){
return (
<div>{this.state.n}<button onClick={this.onClick}>+1</button>
{this.state.n%2===0 ? <div>偶数</div> : <div>奇数</div>}
</div>
)
}
5.render()中的循环
因为循环没有返回值,所有要用一个数组接住循环的结果,或者使用map
render(){
let result =[]
for(let i=0;i<this.state.arr.length;i++){
result.push(this.state.arr[i])
}
return result
}
render(){
return (
<div>
{this.state.arr.map(n=>(<div key={n}>{n}</div>)}
</div>
)
}
componentDidMount()
1.元素插入界面后执行,依赖于DOM
class App extends React.Component{
constructor(props){
super(props)
this.state = {width:undefined}
}
componentDidMount(){
const div = document.getElementById('xx')
const {width} = div.getBoundingClientRect()
//析构赋值 width= div.gBCR().width
this.setState({width})
}
render(){
return (
<div id='xx'>halo {this.state.width}</div>
)
}
}
error:
- 改写state的值直接写 this.state.width = width 应该写setState
- setState对象法后直接console.log,大哥,你这个时候打印的肯定不是setState后的值啊
2.通过React.createRef()来标识div
div中的id很有可能会出现写着写着就重复的情况,所以用ref来写标识
- ref的初始化
this.ref = React.createRef() - ref的读
<div ref={this.ref}> - ref的写
const div = this.ref.current
class App extends React.Component{
ref = undefined //初始化
constructor(){
super()
this.ref = React.createRef() //初始化
this.state = {width:undefined}
}
componentDidMount(){
const div = this.ref.current //写
const {width} = div.getBoundingClientRect()
this.setState({width})
}
render(){
return ( //读取
<div ref={this.ref}>halo {this.state.width}</div>
)
}
}
error:
- ref = undefined 不要写在constructor里面,也不要用let定义
- this.ref = React.createRef() 不要写在state里面,也不要忘记写this
3.与AJAX
此处可以发起加载数据的AJAX请求,官方推荐
4.钩子的执行
首次渲染会执行此钩子
componentDidUpdate()
看官方文档吧
- 在视图更新后执行
- 首次渲染不会执行此钩子
- 若shouldComponentUpdate返回false,则不会触发此钩子
- 可以发起AJAX请求,来更新数据
- 此次写setState可能会引起无线循环,除非用放在if中
componentWillUnmount
- 用于组件将要被移除页面然后被销毁时执行
- unmount过的组件不会再次被mount,你只有去再去创建一次
- 谁污染谁治理
- cDidMount监听了window scroll ,cWillUnmount就要取消监听
- cDM创建了Timer,cWU就要取消Timer
- cDM里面创建了AJAX请求,cWU里面就要取消请求
3.对钩子的总结
- constructor 初始化state
- render 创建虚拟DOM
- shouldComponentUpdate 返回false更新组件,记得要写return true 不写的话,undefined也等同于false
- componentDidMount 组件已经出现在界面
- componentDidUpdate 组件已更新
- componentWillUnmount 组件将死