React 补充

setState 更新状态的其他写法

我们正常使用时很简单的用第一种方法:

 this.setState({

      num:this.state.num + 1

    }

其实后面还可以跟上第二个参数(函数) 在render之后才执行

    this.setState({

      num:this.state.num + 1

    },() =>{

      console.log(this.state.num);

    })

还可以直接写成 函数形式的

this.setState( (state,props) => ({num:props.add * state.num})) 

总结:

    1.对象式的setState是函数式的setState的简写方式(语法糖)

    2.使用原则:

        (1).如果新状态不依赖于原状态 ===> 使用对象方式

        (2).如果新状态依赖于原状态 ===> 使用函数方式

        (3).如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中读取

路由懒加载 

路由激活前再加载  引入的时候写法改变下 其他的一切照旧

正常:import About from './About'

懒加载:const About = lazy(()=> import('./About'))

还可以自定义Loading界面

	//2.通过<Suspense>指定在加载得到路由打包文件前显示一个自定义loading界面
	<Suspense fallback={<h1>loading.....</h1>}>
        <Switch>
            <Route path="/xxx" component={Xxxx}/>
            <Redirect to="/login"/>
        </Switch>
    </Suspense>

Fragment

<Fragment>

<div></div>

<div></div>

<Fragment>

可以不用必须有一个真实的DOM根标签了

Context

一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

首先引入

// 创建Context对象

const MyContext = React.createContext()

// 解构出想用的api  provider提供者统一提供  consumer-用于函数类后代组件

const {Provider,Consumer} = MyContext

渲染子组件时外部要包裹

        <Provider value={ {username,age}}>

            <子组件></子组件>

        </Provider>

 类式组件使用 

静态声明:  static contextType = MyContext

解构引用:  const {username,age} = this.context

 函数组件使用

       <Consumer>// value就是context中的value数据

         {value => `${value.username}---${value.age}`}

       </Consumer>

 完整代码:

import React, { Component } from 'react'
import './index.css'

// Context 用于隔代通讯  一般不用

// 创建Context对象
const MyContext = React.createContext()
// 解构出想用的api  provider提供者统一提供  consumer-用于函数类后代组件 
const {Provider,Consumer} = MyContext

export default class Context extends Component {
  state = {username:'tony',age:24}
  render() {
    const {username,age} = this.state
    return (
      <div className='parent'>
        <h3>我是一代目</h3>
        <h4>本座的名讳是{username}</h4>
        <Provider value={
   
   {username,age}}>
            <D></D>
        </Provider>
      </div>
    )
  }
}

class D extends Component{
  render(){
    return(
          <div className='child'>
      <h3>我是二代目</h3>
      <E></E>
    </div>
    )

  }
}

class E extends Component{
  // 类式 后代组件 生命静态属性接收
  static contextType = MyContext
  render(){
    const {username,age} = this.context
    return(
    <div className='grand'>
      <h3>我是三代目</h3>
      <h4>直接会用初代目的技能{username}--{age}</h4>
      <F></F>
    </div>
    )
  }
}

function F(){
  return(
    <div className='sun'>
       <h3>我是四代(函数组件)</h3>
       <Consumer>
         {value => `${value.username}---${value.age}`}
       </Consumer>
    </div>
  )
}

 PureComponent

用来解决组件  没有意义的render问题 

> 1. 只要执行setState(),即使不改变状态数据, 组件也会重新render()

> 2. 只当前组件重新render(), 就会自动重新render子组件 ==> 效率低

有用的render:只有当组件的state或props数据发生改变时才重新render()

原因:生命周期中的shouldComponentUpdate()总是返回true

  办法1:

    重写shouldComponentUpdate()方法

    比较新旧state或props数据, 如果有变化才返回true, 如果没有返回false

  办法2:  

    使用PureComponent

    PureComponent重写了shouldComponentUpdate(), 只有state或props数据有变化才返回true

    注意:

      只是进行state和props数据的浅比较, 如果只是数据对象内部数据变了, 返回false  

      不要直接修改state数据, 而是要产生新数据

  项目中一般使用PureComponent来优化

完整代码:

import React, { PureComponent } from 'react'
import './index.css'

export default class Parent extends PureComponent {
  state = {carName:'AMG s65',people:['小曲','小魏','小张']}

  newCar = () =>{
    this.setState({
      carName:'M5'
    })
  }

  // shouldComponentUpdate(nextProps,nextState){
	// 	// console.log(this.props,this.state); //目前的props和state
	// 	// console.log(nextProps,nextState); //接下要变化的目标props,目标state
	// 	return !(this.state.carName === nextState.carName)
	// } 

  addp = () =>{
    const {people} = this.state
    people.unshift('老吴')
    this.setState({people})
  }

    addb = () =>{
    const {people} = this.state
    this.setState({people:['老吴',...people]})
  }

  render() {
    console.log('P的render')
    return (
      <div className='parent'>
          <h3>我是parent组件</h3>
          {this.state.carName}
          <button onClick={this.newCar}>点击换车</button>
          <C carName={this.state.carName}></C>
          <div>
            <button onClick={this.addp}>点击增加人数(会认为没变化)</button>
            <button onClick={this.addb}>正确增加老吴</button>
            <br />
            {JSON.stringify(this.state.people)}
          </div>
      </div>
    )
  }
}


class C extends PureComponent {
  
	// shouldComponentUpdate(nextProps,nextState){
	// 	console.log(this.props,this.state); //目前的props和state
	// 	console.log(nextProps,nextState); //接下要变化的目标props,目标state
	// 	return !(this.props.carName === nextProps.carName)
	// } 

  render(){
    console.log('C的render')
    return (
      <div className='child'>
          <h3>我是child组件</h3>
          <span>我接到的车是:{this.props.carName}</span>
          <p>
            为了更高效  有时候子组件数据不需要更新 而触发render  父组件用了 setState 但数据并没有发生变化
            <br /> 一直搞不懂为什么 render 都是执行两次
            <br />解决方式 1 使用shouldComponentUpdate 判断数据变化 返回布尔值 
            <br /> 解决方式2 直接使用 PureComponent 自动比较  但注意写法  对象的地址一样 是不会改变的
          </p>
      </div>
    )
  }
}

render props

  Vue中:

    使用slot技术, 也就是通过组件标签体传入结构  <AA><BB/></AA>

  React中:

    使用children props: 通过组件标签体传入结构

    使用render props: 通过组件标签属性传入结构, 一般用render函数属性

### children props

  <A>

    <B>xxxx</B>

  </A>

  {this.props.children}

  问题: 如果B组件需要A组件内的数据, ==> 做不到

### render props

  <A render={(data) => <C data={data}></C>}></A>

  A组件: {this.props.render(内部state数据)}

  C组件: 读取A组件传入的数据显示 {this.props.data}

完整代码:

import React, { PureComponent } from 'react'
import './index.css'

export default class Parent extends PureComponent {
  state = {carName:'AMG s65',people:['小曲','小魏','小张']}

  newCar = () =>{
    this.setState({
      carName:'M5'
    })
  }

  addp = () =>{
    const {people} = this.state
    people.unshift('老吴')
    this.setState({people})
  }

    addb = () =>{
    const {people} = this.state
    this.setState({people:['老吴',...people]})
  }

  render() {
    console.log('P的render')
    return (
      <div className='parent'>
          <h3>我是parent组件</h3>
          {this.state.carName}
          <button onClick={this.newCar}>点击换车</button>
          
          <div>
            <button onClick={this.addp}>点击增加人数(会认为没变化)</button>
            <button onClick={this.addb}>正确增加老吴</button>
            <br />
            {JSON.stringify(this.state.people)}
          </div>


{/* C内部直接传入D C内部也预留好插槽 */}
          <C carName={this.state.carName} render={ccc => <D ccc={ccc}></D>}></C>
      </div>
    )
  }
}


class C extends PureComponent {
  


  render(){
    const ccc = 123
   // console.log('C的render')
    return (
      <div className='child'>
          <h3>我是child组件</h3>
          <span>我接到的车是:{this.props.carName}</span>
          <p>以下是预留的插槽</p>
          {this.props.render(ccc)}
      </div>
    )
  }
}

class D extends PureComponent {
  
  render(){
    return (
      <div style={
   
   {background:'#f35'}}>
          <h3>我是D组件</h3>
          {this.props.ccc}
      </div>
    )
  }
}

 错误边界

错误边界:用来捕获后代组件错误,渲染出备用页面 不会因为某个组件的错误 导致全部页面渲染不出来

#### 特点:

只能捕获后代组件生命周期产生的错误,不能捕获自己组件产生的错误和其他组件在合成事件、定时器中产生的错误

import React, { Component } from 'react'
import Child from './Child'

export default class Parent extends Component {
  state = {
    hasError:'' // 用于标识子组件是否产生错误
  }

//当Parent的子组件出现报错时候,会触发getDerivedStateFromError调用,并携带错误信息
static getDerivedStateFromError(error){
  console.log('@@@',error)
  return {hasError : error}
}

componentDidCatch(){
  console.log('此处统计错误,反馈给服务器,通知编码人员进行分析解决')
}

  render() {
    return (
      <div>
        <h2>我是Parent组件</h2>
        {this.state.hasError ? <h2>子组件显示不了</h2>: <Child></Child>}
      </div>
    )
  }
}

猜你喜欢

转载自blog.csdn.net/benlalagang/article/details/126848995