React(9)—— Hooks - LazyLoad - Context - 组件优化 - 错误边界 - 组件通信方式总结

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、Hooks
    • 1. React Hooks是什么?
    • 2. 三个常用的Hook
    • 3. State Hook
    • 4. Ref Hook
    • 5. Effect Hook
  • 二、路由懒加载 ------ LazyLoad
  • 三、Fragment
    • 1. 使用
  • 四、Context
    • 1. 理解
    • 2. 使用
    • 3. 注意
  • 五、组件优化
    • 1. Component的问题
    • 2. 效率高的做法
    • 3. 原因
    • 4. 解决
      • 4.1 办法一:重写shouldComponentUpdate()方法
      • 4.2 办法二: 使用PureComponent
  • 六、错误边界
    • 1. 理解
    • 2. 特点
    • 3. 使用方式
    • 4. 示例
  • 七、组件通信方式总结
    • 1. 组件间的关系
    • 2. 几种通信方式
    • 3. 比较好的搭配方式


前言

提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、Hooks

1. React Hooks是什么?

Hook 是 React 16.8 的新增特性。
它可以让你在编写函数式组件时使用 state 以及其他的 React 特性。这里借用阮一峰老师的一句话 —— 钩子(hook)就是 React 函数组件的副效应解决方案,用来为函数组件引入副效应。 函数组件的主体只应该用来返回组件的 HTML 代码,所有的其他操作(副效应)都必须通过钩子引入。

2. 三个常用的Hook

  1. State Hook: React.useState()
  2. Effect Hook: React.useEffect()
  3. Ref Hook:React.useRef()

3. State Hook

  1. State Hook让函数组件也可以有state状态, 并进行状态数据的读写操作
  2. 语法:
 const [状态, 更改状态的方法] = React.useState(initValue)
  1. useState()说明:
    ①useState方法返回的是一个数组[状态,操作状态的方法],因此可以数组结构赋值拿到
    ②数组的结构赋值,只要顺序一致即可,所以名字可以随便取
  2. 案例
import React from 'react'

export default function Plus() {
  const [count,setCount] = React.useState(0)
  
  function add() {
    setCount(count+1)   //写法一
    // setCount((count) => {return count+1})  写法二
  }
  
  return (
    <>
    <h1>当前求和为{count}</h1>
    <button onClick={add}>点我加一</button>
    </>
  )
}  
  1. 总结
  • Plus函数调几次?
    答:它和类组件的render一样,调1+n

  • 第4行也随Plus调用了1+n次,为什么state最终不是0?
    答:React底层在执行useState函数之后,把初始状态保存起来了,
    因此后面在调用useState初始状态0不会覆盖更改后的状态

4. Ref Hook

  1. Ref Hook可以在函数组件中存储/查找组件内的标签或任意其它数据
  2. 语法: const refContainer = useRef()
  3. 作用:保存标签对象,功能与React.createRef()一样
  4. 代码示例
    点击按钮,显示你输入的内容
import React from 'react'

export default function Plus() {
  const myRef = React.useRef()

  function show() {
    alert(myRef.current.value)
  }
  return (
    <>
      <input type="text" ref={myRef}/>
      <button onClick={show}>点我展示输入内容</button>
    </>
  )
}

5. Effect Hook

  • 上面这些钩子,都是引入某种特定的副效应,而useEffect()是通用的副效应钩子 。找不到对应的钩子时,就可以用它。其实,从名字也可以看出来,它跟副效应(side effect)直接相关。
  • 它可以用于模拟类组件中的生命周期钩子
  1. 语法
  React.useEffect(() => {
    1.相当于 conponentDidMount()、conponentDidUpdate()

    return ()=>{ 2.相当于conponentWillUnmount() }
  },[3.监测哪个状态])
  1. 案例
  • 组件挂载,当前求和之后的数字以每秒加1的速度累加
  • 当点击卸载组件按钮时,useEffect会在卸载组件之前清除定时器。
import React from 'react'
import ReactDOM from "react-dom"
export default function Plus() {
const [count, setCount] = React.useState(0)

  React.useEffect(() => {
    let timer = setInterval(() => {
      setCount(count => count+1)
    },1000)
    return () => {
      clearInterval(timer)
    }
  },[])
  
  //卸载组件
  function unMount() {
    ReactDOM.unmountComponentAtNode(document.getElementById("root"))
  }
  return (
    <>
      <h1>当前求和为{count}</h1>
      <button onClick={unMount}>卸载组件</button>
    </>
  )
}
  1. 总结
  • useEffect传入的第一个值:这个函数,有可能相当于componentDidMountcomponentDidUpdatecomponentWillUnmount(甚至有可能同时相当于componentDidMount和componentDidUpdate!)

  • useEffect传入的第二个值:这个[ ],方括号里面是啥(state),它就监测啥(state)

  • useEffect里的参数:
    同时相当于componentDidMount/componentDidUpdate:useEffect只传一个参数useEffect( ()=>{} ) ----------> 它等于谁都监测,组件一挂载和任何状态变动,都会回调那个作为参数的函数
    相当于componentDidMount:useEffect( ()=>{} , [] ) ----------> 第二个参数,[ ]里什么都不写 --------> 相当于谁都不监测
    相当于componentWillUnmout: useEffect( ()=>{ return ()=>{} } ) --------> 第一个参数的返回值的这个函数相当于componentWillUnmout

二、路由懒加载 ------ LazyLoad

用的时候才加载,一般是路由组件进行懒加载
如果不用路由懒加载,页面在第一次进入的时候,就请求了所有组件的数据,如果组件过多,过多的请求这就没有必要了,应该是用户按哪个链接再请求哪个组件

  1. 通过React的lazy函数配合import()函数动态加载路由组件【路由组件代码会被分开打包】
  2. 通过指定在加载得到路由打包文件前显示一个自定义loading界面
import React, { Component, lazy, Suspense} from 'react'
import {NavLink,Route} from 'react-router-dom'

// import Home from './Home'
// import About from './About'

import Loading from './Loading'
const Home = lazy(()=> import('./Home') )
const About = lazy(()=> import('./About'))

export default class Demo extends Component {
  render() {
    return (
      <div>
        <div className="row">
          <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header"><h2>React Router Demo</h2></div>
          </div>
        </div>
        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
              {/* 在React中靠路由链接实现切换组件--编写路由链接 */}
              <NavLink className="list-group-item" to="/about">About</NavLink>
              <NavLink className="list-group-item" to="/home">Home</NavLink>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                <Suspense fallback={<Loading/>}>
                  {/* 注册路由 */}
                  <Route path="/about" component={About}/>
                  <Route path="/home" component={Home}/>
                </Suspense>
              </div>
            </div>
          </div>
        </div>
      </div>
    )
  }
}

三、Fragment

文档碎片

1. 使用

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

import React, { Component,Fragment } from 'react'

export default class Demo extends Component {
  render() {
    return (
      <Fragment kye={1}>
        <input type="text"/>
        <input type="text"/>
      </Fragment>
    )
  }
}

四、Context

上下文

1. 理解

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

2. 使用

  1. 创建Context容器对象
const XxxContext = React.createContext() 
  1. 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:
<xxxContext.Provider value={数据}>
	子组件
</xxxContext.Provider>
  1. 后代组件读取数据:
    第一种方式:仅适用于类组件
static contextType = xxxContext  // 声明接收context
this.context // 读取context中的value数据

第二种方式: 函数组件与类组件都可以

<xxxContext.Consumer>
  {
    value => ( // value就是context中的value数据
      要显示的内容
    )
  }
</xxxContext.Consumer>

3. 注意

在应用开发中一般不用context, 一般都用它来封装react插件

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

//创建Context对象
const MyContext = React.createContext()
const { Provider, Consumer } = MyContext

export default class A extends Component {

  state = {username:'tom',age:18}

  render() {
    const {username,age} = this.state
    return (
      <div className="parent">
        <h3>我是A组件</h3>
        <h4>我的用户名是:{username}</h4>
        <Provider value={{username,age}}>
          <B/>
        </Provider>
      </div>
    )
  }
}

class B extends Component {
  render() {
    return (
      <div className="child">
        <h3>我是B组件</h3>
        <C/>
      </div>
    )
  }
}

/* class C extends Component {
  //声明接收context
  static contextType = MyContext
  render() {
    const {username,age} = this.context
    return (
      

我是C组件

我从A组件接收到的用户名:{username},年龄是{age}

) } } */
function C(){ return ( <div className="grand"> <h3>我是C组件</h3> <h4>我从A组件接收到的用户名: <Consumer> {value => `${value.username},年龄是${value.age}`} </Consumer> </h4> </div> ) }

React(9)—— Hooks - LazyLoad - Context - 组件优化 - 错误边界 - 组件通信方式总结_第1张图片

五、组件优化

1. Component的问题

  1. 只要执行setState(),即使不改变状态数据, 组件也会重新render() ==> 效率低
  2. 只当前组件重新render(), 就会自动重新render子组件,纵使子组件没有用到父组件的任何数据 ==> 效率低

2. 效率高的做法

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

3. 原因

Component中的shouldComponentUpdate()总是返回true(阀门总是打开的)

4. 解决

4.1 办法一:重写shouldComponentUpdate()方法

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

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

4.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:"奔驰c36",stus:['小张','小李','小王']}

  addStu = ()=>{
    const {stus} = this.state
    this.setState({stus:['小刘',...stus]})
  }

  changeCar = ()=>{
    this.setState({carName:'迈巴赫'})


  render() {
    console.log('Parent---render');
    const {carName} = this.state
    return (
      <div className="parent">
        <h3>我是Parent组件</h3>
        {this.state.stus}&nbsp;
        <span>我的车名字是:{carName}</span><br/>
        <button onClick={this.changeCar}>点我换车</button>
        <button onClick={this.addStu}>添加一个小刘</button>
        <Child carName="奥拓"/>
      </div>
    )
  }
}

class Child extends PureComponent {

  render() {
    console.log('Child---render');
    return (
      <div className="child">
        <h3>我是Child组件</h3>
        <span>我接到的车是:{this.props.carName}</span>
      </div>
    )
  }
}

六、错误边界

1. 理解

错误边界(Error boundary):用来捕获后代组件错误,同时将报错的组件替换渲染出备用页面

2. 特点

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

3. 使用方式

getDerivedStateFromError配合componentDidCatch

// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
    console.log(error);
    // 在render之前触发
    // 返回新的state
    return {
        hasError: true,
    };
}

componentDidCatch(error, info) {
    // 统计页面的错误。发送请求发送到后台去
    console.log(error, info);
}

4. 示例

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('此处统计错误,反馈给服务器,用于通知编码人员进行bug的解决');
	}

	render() {
		return (
			<div>
				<h2>我是Parent组件</h2>
				{this.state.hasError ? <h2>当前网络不稳定,稍后再试</h2> : <Child/>}
			</div>
		)
	}
}

七、组件通信方式总结

1. 组件间的关系

  • 父子组件
  • 兄弟组件(非嵌套组件)
  • 祖孙组件(跨级组件)

2. 几种通信方式

  1. props:
    (1).children props
    (2).render props
  2. 消息订阅-发布:
    pubs-sub、event等等
  3. 集中式管理:
    redux、dva等等
  4. Context:
    生产者-消费者模式

3. 比较好的搭配方式

  1. 父子组件:props
  2. 兄弟组件:消息订阅-发布、集中式管理
  3. 祖孙组件(跨级组件):消息订阅-发布、集中式管理、Context(开发用的少,封装插件用的多)

你可能感兴趣的:(react.js,javascript,前端,vue.js,react)