【React】扩展知识点

React知识点扩展

setState(修改数据)
  • 对象式的setState
  • stateChange是修改后的数据对象
  • callback是可选的回调函数
javascript 复制代码
setState(stateChange,[callback])
javascript 复制代码
import React, { Component } from 'react'

export default class Demo extends Component {
  state = { count: 0 }
  add = () => {
    const { count } = this.state
    this.setState({ count: count + 1 },()=>{
    console.log('输出', this.state.count) // 1
    })
    console.log('输出效果', this.state.count) // 0,原因this.setState是异步的
  }
  render() {
    const { count } = this.state
    return (
      <div>
        <h1>当前数字:{count}</h1>
        <button onClick={this.add}>加</button>
      </div>
    )
  }
}
  • 函数式的setState
  • 不用获取原来的state值
  • 新状态依赖原状态的时候,建议使用
  • updater可以接收state, props
  • callback是可选的回调函数
javascript 复制代码
setState(updater,[callback])
javascript 复制代码
  loss = () => {
    this.setState(
      (state, props) => {
        return {
          count: state.count - 1,
        }
      },
      () => {
        console.log('输出', this.state.count) // -1
      }
    )
    console.log('输出效果', this.state.count) // 0
  }
lazyLoad(懒加载)
  • 路由组件的lazyLoad
  • 效果:只有点击的时候才会加载,重复点击不会重复加载
javascript 复制代码
步骤1:import { lazy } from 'react'
const Tab1 = lazy(() => import('./components/tab1'))
const Tab2 = lazy(() => import('./components/tab2'))
  • 使用即可
javascript 复制代码
 <Routes>
     <Route path="/tab1" element={<Tab1 />}></Route>
     <Route path="/tab2/*" element={<Tab2 />}></Route>
     <Route path="*" element={<Navigate to="/tab1" replace />} />
 </Routes>
Hooks
  • hooks是react16.8版本增加的新特性
  • 目的:可以让你在函数组件中使用state以及其它的react特性
(内容存取)React.useState()
  • 第一次初始化指定的值在内部作缓存
  • 返回值,包含两个元素的数组,第一个未内部当前状态,第2个为更新状态值的函数
  • Demo函数执行1+n次
javascript 复制代码
import React, { useState } from 'react'

function Demo() {
  const [name, setName] = useState('乞力马扎罗')
  const [info, setNameInfo] = useState({
    age: '18',
    phone: '18847861437',
  })
  const changeName = () => {
    // 写法一,参数是非函数值
    setName('罗曼蒂克')
    // 写法二,参数是函数
    setName((name) => (name = '罗曼蒂克1'))
  }
  return (
    <div>
      <h4>名称:{name}</h4>
      <h4>年龄:{info.age}</h4>
      <h4>手机号:{info.phone}</h4>
      <button onClick={changeName}>修改名称</button>
    </div>
  )
}

export default Demo
(模拟生命周期和监听)React.useEffect
  • 可以让你在函数组件中用于模拟类组件的生命周期钩子
  • 可以把useEffect Hook 看作如下三个函数的组合
    • componentDidMount()
    • componentDidUpdate()
    • componentWillUnmount()

知识点

  • 定时器的使用和卸载
  • 用函数式更新,避免依赖旧值

javascript 复制代码
import React, { useState, useEffect } from 'react'

function Demo() {
  const [name, setName] = useState('乞力马扎罗')
  const [info, setNameInfo] = useState({
    age: 18,
    phone: '18847861437',
  })
  // useEffect(() => {
  //   console.log('1+n次,页面数据更新几次执行几次')
  // })
  // useEffect(() => {
  //   console.log('监测name,name更新几次执行几次次')
  // }, [name])
  // useEffect(() => {
  //   console.log('监测name和 info.age,他们更新几次执行几次次')
  // }, [name, info.age])
  useEffect(() => {
    console.log('谁也不监测,执行一次,适合存放定时器')
    let timer = setInterval(() => {
      setNameInfo((prevInfo) => {
        // 用函数式更新,避免依赖旧的info
        return { ...prevInfo, age: prevInfo.age + 1 }
      })
    }, 1000)
    //return 这个相当于类组件的在组件卸载前执行
    return () => {
      clearInterval(timer)
    }
  }, [])
  const changeName = () => {
    setName('罗曼蒂克')
  }
  const changeAge = () => {
    setNameInfo({
      ...info,
      age: info.age + 1,
    })
  }
  const changeComponts = () => {}
  return (
    <div>
      <h4>名称:{name}</h4>
      <h4>年龄:{info.age}</h4>
      <h4>手机号:{info.phone}</h4>
      <button onClick={changeName}>修改名称</button>
      <button onClick={changeAge}>修改年龄</button>
      <button onClick={changeComponts}>卸载组件</button>
    </div>
  )
}

export default Demo
(获取ref的值)React.useRef()
javascript 复制代码
import React, { useRef } from 'react'

function Demo() {
  const myRef = React.useRef()
  const changeTip = () => {
    alert(myRef.current.value)
  }
  return (
    <div>
      <input type="text" ref={myRef} />
      <button onClick={changeTip}>点击提示信息</button>
    </div>
  )
}

export default Demo
Fragment(标签)
  • 目的,用于减少div的渲染次数,这个标签在渲染过程中会忽略
  • Fragment只能拥有一个属性,key
  • 可省略Fragment,但是省略后,将不能写key
javascript 复制代码
import React, { Fragment } from 'react'

function Demo() {
  return (
    //可省略 <></>
    <Fragment key="1">
      <div>1111</div>
    </Fragment>
  )
}

export default Demo
React 组件通信种类
Context(组件通信)
  • 目的:一种组件通信方式, 常用于祖组件和后代组件之间的通信
  • 开发中一般不用,一般都用于封装react插件
javascript 复制代码
import React, { Component } from 'react'
import './index.css'
// 步骤1:创建Context容器对象
const MyContext = React.createContext()
const { Provider, Consumer } = MyContext
class A extends Component {
  state = { name: '乞力马扎罗', age: 18 }
  render() {
    return (
      <div className="pd20">
        <h4>祖先组件,Context示例</h4>
        <h4>内容值:{this.state.name}</h4>
        {/* <B /> */}
        {/* 步骤2,从容器对象中获取Provider包裹组件,且必须用value传递值 */}
        <Provider value={{ name: this.state.name, age: this.state.age }}>
          <B />
        </Provider>
      </div>
    )
  }
}
class B extends Component {
  render() {
    return (
      <div className="pd20">
        <h5>父组件</h5>
        <h5>从祖先组件获取值:</h5>
        <C />
        <D />
      </div>
    )
  }
}
// 类组件接收
class C extends Component {
  // 步骤3,类组件中得声明接收使用
  static contextType = MyContext
  render() {
    console.log(this.context, '祖先存储的值')
    return (
      <div className="pd20">
        <h5>类子组件</h5>
        <h5>从祖先组件获取值:{this.context.name}</h5>
      </div>
    )
  }
}
// 函数组件接收
function D(params) {
  return (
    <div className="pd20">
      <h5>函数子组件</h5>
      <h5>
        从祖先组件获取值:
        <Consumer>
          {(value) => {
            // return `${value.name},${value.age}`
            return <span>{value.name},{value.age}</span>
          }}
        </Consumer>
      </h5>
    </div>
  )
}
export default A
组件的问题优化
问题:只要执行setState(),即使不改变状态数据,组件也会重新render()
问题:只要当前组件重新render(),子组件就会重新render,导致效率低
  • 解决方法:应该只有当组件的state或者props数据发生改变的时候才重新render

  • 方法1

javascript 复制代码
  shouldComponentUpdate(nextProps, nextState) {
    console.log(this.props, '目前组件实例自身上的props)')
    console.log(this.state, '目前组件实例自身上的this.state')
    console.log(nextProps, '下一次组件实例自身上的props)')
    console.log(nextState, '下一次组件实例自身上的this.state')
    // 应该只有当组件的state或者props数据发生改变的时候才重新render,比较繁琐
    return !this.state.count === nextState.count
  }
  • 方法2,采用PureComponent
  • PureComponent里面,只是重写了shouldComponentUpdate,比较纯粹的比较
  • 有缺点,修改 数据的时候,如果赋值的对象,类似push和unshift等,不会改变数据
  • 仅支持浅比较,复杂数据类型易失效
  • 若 props/state 是复杂类型(对象、数组),即使内部值变化但引用地址不变,浅比较会判定为 "无变化",导致组件不更新(不符合预期)
javascript 复制代码
import React, { Component, PureComponent } from 'react'
import './index.css'

//因为通过阀门控制的比较繁琐,所以新方法是不用Component,而是用PureComponent
class Parents extends PureComponent {
  state = { count: 1 }
  changeCount = () => {
    const { count } = this.state
    this.setState({
      count: count + 1,
    })
  }
  // 接收两个参数,下一个props状态,下一个state状态
  // shouldComponentUpdate(nextProps, nextState) {
  //   console.log(this.props, '目前组件实例自身上的props)')
  //   console.log(this.state, '目前组件实例自身上的this.state')
  //   console.log(nextProps, '下一次组件实例自身上的props)')
  //   console.log(nextState, '下一次组件实例自身上的this.state')
  //   // 应该只有当组件的state或者props数据发生改变的时候才重新render,比较繁琐
  //   return !this.state.count === nextState.count
  // }
  render() {
    console.log('父组件渲染')
    const { count } = this.state
    return (
      <div className="pd20">
        <h4>父组件</h4>
        <h4>重复render的问题解决</h4>
        <h6>数值:{count}</h6>
        <button onClick={this.changeCount}>加</button>
        <Son count={count} />
      </div>
    )
  }
}
class Son extends Component {
  render() {
    console.log('子组件渲染')
    return (
      <div className="pd20">
        <h4>子组件</h4>
        <h4>子组件接收的值:{this.props.count}</h4>
      </div>
    )
  }
}
export default Parents
renderProps
  • 也叫插槽,
  • 使用children props:通过组件标签传入结构
  • 使用reder props:通过标签属性传入结构,一般用render函数属性

使用children props传入结构后

  • 当我想要a组件里的值传给b组件
  • 发现无法实现
javascript 复制代码
import React, { Component, PureComponent } from 'react'
import './index.css'

class Parents extends Component {
  render() {
    return (
      <div className="pd20">
        <h4>父组件</h4>
        <h4>renderProps</h4>
        <A>
          {/* 标签体内容 */}
          111111
          <B></B>
        </A>
      </div>
    )
  }
}
class A extends Component {
  state = { name: '乞力马扎罗' }
  render() {
    return (
      <div className="pd20">
        <h4>子组件</h4>
        {/* 要想渲染标签体内容,必须这样调用 */}
        {/* 这里想给B组件传name,发现无法实现 */}
        {this.props.children}
      </div>
    )
  }
}
class B extends Component {
  render() {
    return (
      <div className="pd20">
        <h4>孙子组件</h4>
      </div>
    )
  }
}
export default Parents
  • 解决方法,通过renderProps传递B组件
  • 优点,你只管留位置,下回想换组件,我直接在调用方修改
javascript 复制代码
import React, { Component } from 'react'
import './index.css'

class Parents extends Component {
  render() {
    return (
      <div className="pd20">
        <h4>父组件</h4>
        <h4>renderProps</h4>
        {/* 给A传递的不是一般的props,是renderProps,这里可以传B,可以传任何你想渲染的组件 */}
        <A render={(name) => <B name={name} />}></A>
      </div>
    )
  }
}
class A extends Component {
  state = { name: '乞力马扎罗' }
  render() {
    const { name } = this.state
    return (
      <div className="pd20">
        <h4>A组件</h4>
        {/* 这里相当于预留位置,具体预留给谁,看上面render谁,就留给谁,介个就是插槽 */}
        {this.props.render(name)}
      </div>
    )
  }
}
class B extends Component {
  render() {
    console.log(this.props, 'A组件传的值')
    const { name } = this.props
    return (
      <div className="pd20">
        <h4>B组件</h4>
        <h4>A组件通过renderProps传过来的值:{name}</h4>
      </div>
    )
  }
}
export default Parents
错误边界(备用页面)
  • 用来捕获后代组件错误,渲染出备用页面
  • 特点,只能捕获后代组件生命周期产生的错误,不能捕获自己组件中产生的错误和其它组件在合成事件,定时器中产生的错误

父组件

javascript 复制代码
import React, { Component } from 'react'
import Children from './children'
import './index.css'
export default class Parents extends Component {
  state = {
    hasError: '', //用于标识子组件是否产生错误
  }
  // 这个生命周期的设计目的就是通过返回一个对象来更新组件的 state,只有键名和 state 中的属性一致,就会修改
  // Parents的子组件出现报错会触发它的调用并携带错误信息
  static getDerivedStateFromError(error) {
    return {
      hasError: error,
    }
  }
  // 子组件(包括嵌套子组件)在 render 阶段、生命周期方法中抛出未捕获的错误时触发
  componentDidCatch(error,info) {
    console.log('这里可以统计错误次数',error,info)
  }
  render() {
    return (
      <div className="pd20">
        <h4>父组件</h4>

        {this.state.hasError ? (
          <h2>当前网络不稳定,请稍后重试</h2>
        ) : (
          <Children />
        )}
      </div>
    )
  }
}

子组件

javascript 复制代码
import React, { Component } from 'react'

export default class Child extends Component {
  state = {
    // users: [
    //   {
    //     name: '乞力马扎罗',
    //     age: 18,
    //   },
    // ],
    users: '999',
  }
  render() {
    return (
      <div className="pd20">
        <h4>子组件</h4>
        {this.state.users.map((item) => {
          return (
            <div key={item.name}>
              <h4>{item.name}</h4>
            </div>
          )
        })}
      </div>
    )
  }
}
相关推荐
怕浪猫18 小时前
React从入门到出门第四章 组件通讯与全局状态管理
前端·javascript·react.js
欧阳天风18 小时前
用setTimeout代替setInterval
开发语言·前端·javascript
EndingCoder18 小时前
箭头函数和 this 绑定
linux·前端·javascript·typescript
xkxnq18 小时前
第一阶段:Vue 基础入门(第 11 天)
前端·javascript·vue.js
小oo呆18 小时前
【自然语言处理与大模型】LangGraphV1.0入门指南:核心组件Nodes
前端·javascript·easyui
行走的陀螺仪18 小时前
在UniApp H5中,实现路由栈的持久化
前端·javascript·uni-app·路由持久化·路由缓存策略
ProgramHan18 小时前
React 19 新特性深度解析:告别 useEffect 的时代
前端·react.js·前端框架
南玖i18 小时前
SuperMap iServer + vue3 实现点聚合 超简单!
javascript·vue.js·elementui
泰勒疯狂展开18 小时前
Vue3研学-标签ref属性与TS接口泛型
前端·javascript·vue.js