React基础知识整理学习四

props校验-基本使用

使用步骤

  • 导入 prop-types 包 。这个包在脚手架创建项目时就自带了,无须额外安装import PropTypes from 'prop-types' 这里的PropTypes可以改成其他的名字.

  • 使用组件名.propTypes = {属性名1: 类型1, ...} 来给组件的props添加校验规则,这里的propTypes是固定写法.

import PropTypes from 'prop-types'
class App extends React.component {
  render(){
    return (
    	<h1>Hi, {props.colors}</h1>
    )
  }
}
App.propTypes = {
    // 约定colors属性为array类型
    // 如果类型不对,则报出明确错误,便于分析错误原因
    colors: PropTypes.array.isRequired
}
复制代码

常见规则

(1) 常见类型:array、bool、func、number、object、string

(2) React元素类型:element

(3) 必填项:isRequired

(4) 特定结构的对象:shape({ })

Com.propTypes = {
  fn: PropTypes.func, // fn 是函数类型
  isEdit: PropTypes.bool.isRequired // isEdit的类型是bool,且必须要传入
  
  // 特定结构的对象
  goodInfo: PropTypes.shape({ // goodInfo的格式一个对象,有 数值price属性 和 字符串name属性
    price: PropTypes.number,
    name: PropTypes.string
  })
}
复制代码

props默认值

  • defaultProps

定义组件:

class App extends React.Component{
    return (
        <div>
            此处展示props的默认值:{ this.props.pageSize }
        </div>
    )
}
// 设置默认值
App.defaultProps = {
	pageSize: 10
}
// 类型限制
App.propTypes = {
  pageSize: PropTypes.number
}
复制代码

使用组件:

// 不传入pageSize属性
<App />
复制代码
  • 解构赋值的默认值
class App extends React.Component{
    return (
     // 解构赋值的默认值
     const { pageSize = 10} = this.props
        <div>
            此处展示props的默认值:{ this.props.pageSize }
        </div>
    )
}
// 类型限制
App.propTypes = {
  pageSize: PropTypes.number
}
复制代码

props校验和默认值简化-类的静态成员-static

什么是静态成员

  • 静态成员:通过类或者构造函数本身才能访问的属性或者方法
  • 实例成员: 通过实例调用的属性或者方法
class Person {
  constructor(name){
    this.name = name // 实例成员
  }
  // 方法1:在class内部 通过static来定义
  static n = 1 // 静态成员 有static修饰
}
// 方法2 在class外部 通过类型.属性名来定义
Person.m = function() { console.log()}
复制代码

访问:

Person.n
Person.m()
复制代码

简化props校验和默认值

把校验规则和默认值放入class内部

组件生命周期

什么是生命周期

  • 生命周期:一个事物从创建到最后消亡经历的整个过程

  • 组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程

什么是钩子函数

在生命周期的不同阶段,会自动被调用执行的函数,为开发人员在不同阶段操作组件提供了时机。

(注:只有类组件 才有生命周期钩子函数)

image.png

图片地址:projects.wojtekmaj.pl/react-lifec…

组件生命周期-挂载阶段

  • 执行时机:组件创建时(页面加载时)
  • 执行顺序:constructor() -> render() -> componentDidMount()

image.png

组件生命周期-更新阶段

  • 执行顺序: render() -> componentDidUpdate()

image.png

如果有些事情是需要每次更新去做的,就可以写在componentDidUpdate中。例如:数据本地存储

三种操作可触发组件更新

  • 调用setState。它能改数据&& 更新页面
  • 调用forceUpdate()
  • 组件接收到新的props

组件生命周期-卸载阶段

  • 执行时机: 组件销毁

image.png

/* eslint-disable react/prop-types */
import React, { Component, createRef } from 'react'
import ReactDOM from 'react-dom'

class Son extends Component {
  constructor () {
    super()

    this.timer = setInterval(() => {
      console.log('子组件:', Date.now())
    }, 1000)
    this.fn = () => {
      console.log('鼠标移动...')
    }
    window.addEventListener('mousemove', this.fn)
  }

  render () {
    return <div>子组件:{this.props.content}</div>
  }

  componentDidUpdate () {
    console.log('子组件: componentDidUpdate')
  }

  componentWillUnmount () {
    console.log('子组件 卸载: componentWillUnmount')
    // 删除事件
    window.removeEventListener('mousemove', this.fn)
    // 删除定时器
    clearInterval(this.timer)
  }
}

export default class App extends Component {
  constructor () {
    super()
    console.log('1. constructor')
    this.state = {
      content: '',
      isShow: true
    }
    this.refTxt = createRef()
  }

  hChange = (e) => {
    this.setState({ content: e.target.value })
  }

  click = () => {
    this.forceUpdate()
  }

  render () {
    console.log('2. render')
    return (
      <div>
        <button onClick={this.click}>更新</button>
        组件<input value={this.state.content} onChange={this.hChange} />
        <br />
        {this.state.isShow && <Son content={this.state.content} />}
      </div>
    )
  }

  componentDidMount () {
    console.log('3. componentDidMount')
    console.log(this.refTxt.current)

    // axios.get()
  }

  componentDidUpdate () {
    console.log('更新完成: componentDidUpdate')
  }
}

ReactDOM.render(<App />, document.getElementById('root'))
复制代码

setState进阶

setState的特点

  • 可以表现为异步:setState调用之后,并不会立即去修改state的值,也不会立即去更新dom
  • 多次调用会合并。setState({对象})会合并,再统一触发一次render()
  • 使用不当会死循环。在componentDidUpdate, render 中调用setState()会导致死循环
import React, { Component } from 'react'
export default class App extends Component {
  state = {
    n: 1
  }

  hClick = () => {
    this.setState({ n: 100 })
    console.log(this.state.n)
    console.log(document.getElementById('span').innerHTML) // 这会打印?1
  }

  render () {
    return (
      <div>
        <button onClick={this.hClick}>click</button>
        <span id="span">n: {this.state.n}</span>
      </div>
    )
  }
}

复制代码

setState的第二个参数

(1) 格式一

this.setState({} [,回调函数])

回调函数是可选的,它的作用是:当本轮setState生效(state更新,页面ui更新)之后,会调用回调函数

import React, { Component } from 'react'
export default class App extends Component {
  state = {
    n: 1
  }

  hClick = () => {
    this.setState({ n: this.state.n + 1 }, () => {
      console.log(this.state.n) // 4
    })
    this.setState({ n: this.state.n + 2 }, () => {
      console.log(this.state.n)  // 4
    })
    this.setState({ n: this.state.n + 3 }, () => {
      console.log(this.state.n)  // 4
    })
  }

  render () {
    return (
      <div>
        <button onClick={this.hClick}>click</button>
        <span id="span">n: {this.state.n}</span>
      </div>
    )
  }
}
复制代码

(2) 格式二

this.setState((上一状态) => {
  return 新状态
}[,回调函数])
复制代码

回调函数是可选的,它的作用是:当本轮setState生效(state更新,页面ui更新)之后,会调用回调函数

import React, { Component } from 'react'
export default class App extends Component {
  state = {
    n: 1
  }

  hClick = () => {
    this.setState(
      (preState) => ({ n: preState.n + 1 }),
      () => {
        console.log(this.state.n)  // 7
      }
    )
    this.setState(
      (preState) => ({ n: preState.n + 2 }),
      () => {
        console.log(this.state.n)  // 7
      }
    )
    this.setState(
      (preState) => ({ n: preState.n + 3 }),
      () => {
        console.log(this.state.n)  // 7
      }
    )
  }

  render () {
    return (
      <div>
        <button onClick={this.hClick}>click</button>
        <span id="span">n: {this.state.n}</span>
      </div>
    )
  }
}
复制代码

setState进阶-同步or异步

setState本身并不是一个异步(setTime, setInterval, ajax,Promise.then.....)方法,其之所以会表现出一种异步的形式,是因为react框架本身的性能优化机制.

(在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用 setState 不会同步更新 this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过 addEventListener 直接添加的事件处理函数,还有通过setTimeout || setInterval 产生的异步调用。)

简而言之:

  • 经过React 处理(事件回调,钩子函数)中的setState是异步更新
  • 没有经过React处理(通过 addEventListener || setTimeout/setInterval)中的setState是同步更新

image.png

import reactDom from 'react-dom'

import React, { Component } from 'react'

class App extends Component {
  state = {
    n: 1
  }

  // setTimeout(() => {
  //   this.setState({ n: 2 })
  //   console.log(this.state.n)
  //   console.log(document.getElementById('btn').innerHTML)
  // }, 2000)

  componentDidMount () {
    // this.setState({ n: 2 })
    // console.log(this.state.n)
    // console.log(document.getElementById('btn').innerHTML)

    document.getElementById('btn').addEventListener('click', () => {
      this.setState({ n: 2 })
      console.log(this.state.n)
      console.log(document.getElementById('btn').innerHTML)
    })
  }

  click = () => {
    this.setState({ n: 2 })
    console.log(this.state.n)
    console.log(document.getElementById('btn').innerHTML)
  }

  render () {
    return (
      <div>
        {/* <button id="btn" onClick={this.click}> */}
        <button id="btn" onClick={this.click}>
          {this.state.n}
        </button>
      </div>
    )
  }
}

reactDom.render(<App />, document.getElementById('root'))

复制代码

参考链接 :

zhuanlan.zhihu.com/p/158725289

segmentfault.com/a/119000001…

猜你喜欢

转载自juejin.im/post/7030786221974487077