React组件化开发

8 阅读3分钟
生命周期函数
1.如果不初始化state或不进行方法绑定,则不需要React组件实现构造函数
2.constructor:通过this.state赋值对象来初始化内部的state  为事件绑定this

组件之间的通信
父组件通过属性=值的形式来传递给子组件数据
子组件通过props参数获取父组件传递过来的数据

开发中不能直接通过修改state的值来让界面发生更新
React并没有实现类似Vue的监听数据变化,必须通过setState来告知react数据已经发生变化

setState异步更新
changeText() {
    this.setState({
        message: '123'
    })
    console.log(this.state.message) // hello word
}
setState设计为异步,可以显著提升性能
    1.如果每次调用setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,效率很低
    2.最好的方法是获取多个更新,之后进行批量更新
如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步
    state和props不能保持一致性,会在开发中产生很多问题
    
获取异步更新后的结果
// 1.回调函数,类似nextTick
this.setState({
    message: '123'
},()=>{
    console.log(this.state.message) // 123
})
// 2.获取异步更新的state  界面已经发生更新
componentDidUpdate(){
    console.log(this.state.message)
}

setState同步的情况
在组件生命周期和React合成事件中,setState是异步
在setTimeout或者原生dom事件中,setState是同步
changeText() {
    setTimeout(()=>{
        this.setState({
            message: '你好,李银河'
        })
        console.log(this.state.message) // 你好啊,李银河
    }, 0)
}
componentDidUpdate(){
    // 如果放外面就是异步
   const btn = document.getElementByid('btn')
   btn.addEventListener('click',()=>{
    this.setState({
        message: '你好,李银河'
    })
    console.log(this.state.message) // 你好啊,李银河
   })
}

setState的数据合并
this.state = {
    message: '123',
    name: 'coder'
}
changeText() {
    this.setState({
        message: '你好,李银河'
    })
}
// 不会改变name的值
// 相当于 Object.assign({},this.state, {message: '你好,李银河'})
setState合并累加
this.setState((prevState,props)=>{
    return {
        counter: prevState.counter+1
    }
})

React更新机制
渲染流程:JSX->虚拟dom->真实dom
更新流程:
props/state改变 -> render函数重新执行 -> 产生新的dom树-> 新旧dom树diff算法 ->计算出差异进行更新 -> 更新到真实dom
1.同层节点之间互相比较,不会跨节点比较
2.不同类型的节点,产生不同的树结构
3.开发中,通过key来指定哪些节点在不同的渲染下保持稳定

使用key注意事项
key应该是唯一的
key不要使用随机数(随机数在下一次render时,会重新生成一个数字)
使用index作为key,对性能是没有优化的

shouldComponentUpdate
nextProps 修改之后最新的props属性
nextState 修改之后最新的state
返回一个boolean类型
true 需要调用render方法
false 不需要调用render方法

shouldComponentUpdate(nextProps, nextState){
    if(nextState.count !== this.state.count){
        return true;
    }
    return false;
}

pureComponent
export default class App extends PureComponent {}
//函数组件怎么包裹
const MemoHeader = memo(fucntion Header(){
    return <h2>我是header</h2>
})

ref用法
函数式组件没有实列,无法通过ref获取他们的实列
constructor(props){
    super(props);
    this.titleRef = createRef()
    this.titleRef2 = null
}
<div ref={this.titleRef}>123</div>
<div ref={el => this.titleRef2 = el}>456</div>

this.titleRef.current.style.color = 'red'
this.titleRef2.style.color = 'blue'

通过forwardRef高阶函数转发
const ForwardProfile = forwardRef(function(props, ref) {
    return (
        <div>
            <h1 ref={ref}>Profile</h1>
        </div>
    )
})

高阶函数:接受一个或多个函数作为输入,输出一个函数 filter,map
高阶组件:参数为组件,返回值为新组件的函数
高阶组件不是一个组件,而是一个函数,这个函数的参数是一个组件,
返回值也是一个组件

function higherOrderComponent(wrappercomponent) {
    class Newcomponent extends Purecomponent {
        render() {
            return <wrappercomponent>
        }
    }
    return Newcomponent
}

Portals
渲染的内容独立于父组件,甚至是独立于当前挂载到的DOM元素中
class Modal extends PureComponent{
    render(){
        return ReactDOM.createPortal(
            this.props.children,
            document.getElementById("modal")
        )
    }
}

Fragment 类似<template>
<Fragment></Fragment>或<></>

严格模式
<React.StrictMode>
  <App/>
</React.StrictMode>
1.识别不安全的生命周期
2.使用过时的ref API
3.使用废弃的findDOMNode方法
4.检查意外副作用  组件的constructor会被调用两次,生产环境不会