美文网首页前端
React类组件详解

React类组件详解

作者: 是周大侠啊 | 来源:发表于2022-03-18 21:42 被阅读0次

    两种方式创建Class组件

    ES5方式(过时)

    import React from 'react'
    
    const A = React.createClass({
      render(){
        return (
          <div>hi</div>
        )
      }
    })
    export default A
    
    // 由于 ES5 不支持 class,才会有这种方式
    

    ES6 方式

    class B extends React.Component {
      constructor(props) {
        super(props);
      }
    
      render() {
        return (
          <div>hi</div>
        )
      }
    }
    export default B
    // extends,constructor,super 强行记忆
    

    以后只用class方式创建类组件
    webpack+babel将ES6翻译成ES5

    Props-外部数据

    一般外部数据都是来自父元素的属性
    传入props给B组件

    class Parent extends React.Component {
      constructor(props) {
        super(props);
        this.state = { name: 'ories' }
      }
      onClick = ()=> {}
      render() {
        return <B name={this.state.name} onClick={this.onClick}>hi</B>
      }
    }
    export default B
    // name, onClick就是外部数据
    // 外部数据被包装为一个对象, {name: 'frank', onClick:..., children:'hi'}
    // 此处的 onClick 是一个回调
    
    props初始化

    初始化

    class B extends React.Component {
      constructor(props){
        super(props);
      }
      render(){}
    }
    
    • 要么不初始化,即不写constructor
    • 要么初始化,必须写全,不写super直接报错
    • 这么做了之后,this.props就是外部数据对象的地址了
    读取props

    读取

    class B extends React.Component {
      constructor(props) {
        super(props);
      }
      render(){
        return (<div onClick={this.props.onClick}>
          {this.props.name}
          <div>
          {this.props.children}
          </div>
        </div>
        )
      }
    }
    // 通过 this.props.xxx 读取
    
    不准写Props
    • 给props的值(一个地址)

    • this.props = {/另一个对象/}

    • 不要写这样的代码,没有意义

    • 理由: 既然是外部数据,就应该由外部更新

    • 该props的属性

    • this.props.xxx = 'hi'

    • 不要写这样的代码,没有意义

    • 理由: 既然是外部数据,就不应该从内部改值

    • 原则

    • 应该由数据的主人对数据进行更改

    相关钩子

    component WillReceiveProps钩子
    当组件接受新的props时,会触发此钩子
    钩子就是特殊函数,属于行业黑话
    例子

    import React from "react";
    import ReactDOM from "react-dom";
    
    
    class App extends React.Component {
    
    
      constructor(props) {
        super(props);
        this.state = {x: 1}
      }
    
      onClick = ()=>{
        this.setState({
          x: this.state.x + 1
        })
      }
    
      render(){
        return <div className="App">
          App <button onClick={this.onClick}>+1</button>
          <B name={this.state.x}></B>
        </div>
      }
    }
    
    class B extends React.Component {
      componentWillReceiveProps(newProps, nextContext) {
        console.log('旧的 props', this.props)
        console.log('props变化了')
        console.log('新的props')
        console.log(newProps)
      }
      render(){
        return <div>
          {this.props.name}
        </div>
      }
    }
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    
    • 该钩子已经被弃用,总之,不要使用这个钩子
    props的作用
    • 接受外部-数据
    • 只能读不能写
    • 外部数据由父组件传递
    • 接受外部-函数
    • 在恰当的时机,调用该函数
    • 该函数一般是父组件的函数

    内部数据State

    • State & setState
    • 初始化State,代码
    class B extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          user: {name: 'frank', age: '18'}
        }
      }
      render() {/* ... */}
    }
    
    读写State

    读用this.state, this.state.xxx.yyy.zzz
    写用this.setState(???, fn)
    注意setState不会立即改变this.state,会在当前代码运行完后,再去更新this.state,从而触发UI更新
    this.setState((state, props)=> newState, fn)
    以上的代码反而更容易理解state
    fn会在写入成功后执行
    写的时候会shallow merge, setState会自动将新state与旧state进行一级合并
    改写+1的例子,set的时候尽量用函数的形式,能用就用

    import React from "react";
    import ReactDOM from "react-dom";
    
    
    class App extends React.Component {
    
      constructor(props) {
        super(props);
        this.state = {x: 1}
      }
    
      onClick = ()=>{
        this.setState({
          x: this.state.x + 1
        })
        this.setState({
          x: this.state.x + 1
        })
        // 只会加1
      }
    
      onClick2 = ()=>{
        this.setState((state)=>({x: state.x + 1}))
        this.setState((state)=>({x: state.x + 1}))
        // 会加2
      }
    
    
      render(){
        return <div className="App">
          App <button onClick={this.onClick2}>+1</button>
          <B name={this.state.x}></B>
        </div>
      }
    }
    
    class B extends React.Component {
      componentWillReceiveProps(newProps, nextContext) {
        console.log('旧的 props', this.props)
        console.log('props变化了')
        console.log('新的props')
        console.log(newProps)
      }
      render(){
        return <div>
          {this.props.name}
        </div>
      }
    }
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<App />, rootElement);
    

    生命周期

    • 类比的如下代码
      let div = document.createElement('div') //这是div的create/construct过程
      div.textContent='hi' // 这是初始化state
      document.body.appendChild(div)/ //这是div的mount过程
      div.textContent = 'hi2' //这是div的update过程
      div.remove() // 这是div的unmount过程

    同理react组件也有这些过程,我们称之为生命周期
    react有如下生命周期
    函数列表,带的重要,不带的不常用,

    *constructor() //构造初始化
    static getDerivedStateFromProps()
    *shouldComponentUpdate() //组件确认更新修改
    *render() //组件渲染
    getSnapshotBeforeUpdate()
    *componentDidMount() //组件挂载
    *componentDidUpdate() //组件被更新之后
    *componentWillUnmount() //组件将要卸载
    static getDerivedStateFromError() 
    componentDidCatch()
    

    React常用生命周期

    函数列表

    constructor()-在这里初始化state
    shouldComponentUpdate()-return false阻止更新
    render()-创建虚拟dom
    componentDidMount()-组件已出现在页面
    componentDidUpdate()-组件已更新
    componentWillUnmount()-组件将死
    
    constructor
    • 用途:
      初始化props
      初始化state,但此时不能调用setState
      用来写bind this
    constructor() {
      /* 其他代码略 */
      this.onClick = this.onClick.bind(this)
      可以用新语法代替
      onClick = ()=> {}
      constructor(){ /* 其他代码略 */ }
    }
    
    shouldComponentUpdate
    • 用途
      返回true表示不阻止UI更新
      返回false表示阻止UI更新
    • 面试常问: shouldComponentUpdate有什么用?
      答: 它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要的更新

    这样写render会重复执行

    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          n: 1
        }
      }
    
      onClick = ()=>{
        this.setState(state=> ({
          n: state.n+1
        }))
        this.setState(state=> ({
          n: state.n-1
        }))
      }
      
      render(){
        console.log('render了一次')
        return <div>
          App
          <div>
            {this.state.n}
            <button onClick={this.onClick}>+1</button>
          </div>
        </div>
      }
    }
    

    这样写render就不会重复执行

    ...以上代码省略
     shouldComponentUpdate(newProps, newState) {
        if(newState.n === this.state.n) {
          return false
        } else {
          return true
        }
      }
    
      render(){
        console.log('render了一次')
        return <div>
          App
          <div>
            {this.state.n}
            <button onClick={this.onClick}>+1</button>
          </div>
        </div>
      }
    }
    
    • 启发,所有组件是否都要加这个功能
      React.PureComponent,新旧state做一层浅对比,以及新 props 和旧 props 的每一个 key。
      如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。
      大多数情况下能用PureComponent,就尽量用PureComponent,除非遇到了bug
    生命周期render用途
    • 展示视图,return(<div>...</div>),return一个虚拟dom
    • 只能有一个根元素
    • 如果有两个根元素,就要用<React.Fragment>包起来,<React.Fragment>可以缩写成<></>
    生命周期render技巧
    • render里面可以写if...else
    • render里面可以写?:表达式
    • render里面不能写for循环,需要用数组
    • render里面可以写array.map(循环)
    生命周期componentDidMount()用途(组件挂载之后的操作,也是请求ajax的地方)
    • 在元素插入页面后执行代码,这些代码依赖DOM
    • 比如获取div的高度
    • 此处可以发起加载数据的AJAX请求
    • 首次渲染会执行此钩子
    • 参数没有
    react提供了更方便的方式获取div

    this.divRef = React.createRef()
    <div ref={this.divRef}>hi</div>

    componentDidMount(){
      const div = this.divRef.current
    }
    
    生命周期componentDidUpdate()的用途
    • 在视图更新后执行代码
    • 此处也可以发起AJAX请求,用于更新数据
    • 首次渲染不会执行此钩子
    • 在此处setState可能会引起无限循环,除非放在if里
    • 若shouldComponentUpdate反回false,则不触发此钩子
    • 接受之前的props,之前的state,和snapshot参数一般不用
    componentWillUnmount的用途
    • 组件将要被移出页面然后被销毁时执行的代码
    • unmount过的组件不会再次mount

    componentWillUnmount举例
    如果在componentDidMount里面监听了window scroll,那么就要在componentWillUnmount里面取消监听
    如果在componentDidMount里面创建了Timer,就要在componentWillUnmount里面取Timer
    如果在componentDidMount里面创建AJAX请求,就要在componentWillUnmount里面取消请求,请求没回来就关闭页面

    总结钩子

    constructor() - 在这里初始化state
    shouldComponentUpdate() - return false 阻止跟新
    render() - 创建虚拟DOM
    componentDidMount() - 组件已经出现在页面里面
    componentDidUpdate() - 组件已更新
    componentWillUnmount() - 组件将死
    

    钩子的渲染过程图


    React钩子机制.jpg

    相关文章

      网友评论

        本文标题:React类组件详解

        本文链接:https://www.haomeiwen.com/subject/nhjndrtx.html