跳至主要內容

8.4 React高级 🎉

刘春龙...大约 8 分钟RRACTWEB前端react

8.4 React高级 🎉

Context应用 💎

React组件中数据是通过 props 属性自上而下(由父及子)进行传递的,Context也可以传递数据,props 是父传子,但是假如子组件还有子组件的话,就构成了(父亲-儿子-孙子)的关系了,如果把父亲的数据传递给孙子,使用Context无疑是最好的选择,它可以跨越组件的层级关系。

首先看一下props 传递的方式

import React, { Component } from 'react'
import Child from "./child"
class Parent extends Component {
  constructor(props) {
    super(props)
    this.state = {
      user: {
        name: 'lcl',
        age: 30
      }
    }
  }
  render() {
    return (
      <div>
        <p>父亲</p>
        <Child value={this.state.user} />
      </div>
    )
  }
}
export default Parent

通过以上操作,数据就从父组件借助儿子组件传递到了孙子组件中

再看用Context的传递方式

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。如果我们要在组件树中去共享某些数据,并且要避免通过中间元素传递 props,则可以使用Context来实现。

  • 创建Context对象
React.createContext(defaultValue)
  • 使用ContextProvider组件
    Contenxt的Provider组件用来提供其它组件要共享的数据。

设置value属性来设置要共享的数据。

我们把要使用共享数据的组件称为消费组件

<MyContext.Provider value={/* 某个值 */}>
    <Home />   
</MyContext.Provider>

这样<父组件 /> 组件下的所有子组件都能拿到value的值

  • 添加contextType属性
MyClass.contextType = MyContext;

提示

挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext()创建的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中。

使用方式

首先,创建Context对象,把他单独整理成一个插件

// /src/utils/MyContext.jsx
import React from 'react'
export default React.createContext("随便");

下面就能传递数据了,任意子组件均可获取

import React, { Component } from 'react'
import Child from "./child"
import MyContext from './utils/MyContext';
class Parent extends Component {
  constructor(props) {
    super(props)
    this.state = {
      user: {
        name: 'lcl',
        age: 30
      }
    }
  }
  render() {
    return (
      <div>
        <p>父亲</p>
        <MyContext.Provider value={this.state.user}>
          <Child />
        </MyContext.Provider>
      </div>
    )
  }
}
export default Parent

注意

函数组件也是可以订阅Context的,这里我只说类组件的使用方式,后面我们为你学习了ReactHook后使用更为简单的方式进行传递

Fragments 💎

react中的Fragments,就好比vue中的template或者小程序中的block,只会起到包裹的作用,并不会真正的渲染dom结构

import React from 'react'
const Parent = () => {
  const dom1 = (
    <div>
      <p>1</p>
      <p>2</p>
    </div>
  )
  const dom2 = (
    <React.Fragment>
      <p>3</p>
      <p>4</p>
    </React.Fragment>
  )
  return (
    <div>
      {dom1}
      {dom2}
    </div>
  )
}
export default Parent

最后,打开控制台,查看页面生成的dom结构

Fragments短语法

使用<></>代替<React.Fragment></React.Fragment>

const dom2 = (
    <>
      <p>3</p>
      <p>4</p>
    </>
)

因为Fragments最终不会被渲染为DOM,所以不要在Framents上面绑定事件或者设置一些其它属性,目前只支持设置key属性。

错误边界 💎

通常某一个组件中发生的错误会导致整个应用崩溃,页面一片空白。

仅仅一个小组件的报错,让整个项目应用都无法显示,这就身份不可合理了,如果我们想让未出现错误的组件还能继续渲染,则可以使用错误边界错误边界是一种 React 组件,可以捕获并打印发生在其子组件树任何位置的 JavaScript 错误,并且,它可以渲染出备用 UI,在渲染期间、生命周期方法和整个子组件树的构造函数中捕获错误。

第一步,定义一个错误边界的组件:

class 组件中定义了 static getDerivedStateFromError()componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么这个组件就变成一个错误边界的组件。

// /src/utils/error.jsx
import React from 'react';
class Error extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            error: null,
            errorInfo: null
        }
    }
    componentDidCatch(error, errorInfo) {
        // 捕获到子组件树当中发生的错误时调用
        this.setState({
            error: error,
            errorInfo: errorInfo
        })
    }
    render() {
        return (
            <div>
                {
                    this.state.errorInfo
                        ?
                        <div>
                            <p>组件发生错误</p>
                            <p>错误信息如下</p>
                            <p>  {this.state.error && this.state.error.toString()}</p>
                            <p> {this.state.errorInfo.componentStack}</p>
                        </div>
                        :
                        this.props.children
                }
            </div>
        )
    }
}
export default Error
import React from 'react'
import Error from './utils/error'
import Child from './child'
import Sunzi from './sunzi'
const Parent = () => {
  return (
    <div>
      <p>我是父亲,如果我的后代有错误,我依旧正常显示</p>
      <Error>
        <Child />
      </Error>
      <Error>
        <Sunzi />
      </Error>
    </div>
  )
}
export default Parent

虽然子组件有错误,但不影响其他组件的显示

错误边界无法捕获以下场景中产生的错误:

  • 事件处理
  • 异步代码(例如 setTimeoutrequestAnimationFrame 回调函数)
  • 它自身抛出来的错误(并非它的子组件)

解决办法用:

  • try catch

  • window.onerror

    window.onerror可以捕捉语法错误,也可以捕捉运行时错误,只要在当前window执行的Js脚本出错都会捕捉到。

    window.onerror=function(err){
      console.log(err)
    }
    

Refs操作DOM 💎

Refs 提供了一种方式,允许我们访问DOM 元素。

<div ref={ref}></div>
  • 创建 Refs

    使用 React.createRef() 创建的。

    通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。

    constructor(props) {
      super(props);
      this.myRef = React.createRef();
    }
    
  • 使用Refs

    给对应的React 元素设置ref属性,则相当于使用 ref 去存储 DOM 节点的引用。

    render() {
       return <input ref={this.myRef} />;
    }
    
  • 访问Refs

    当 ref 被传递给 render 中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问。

    componentDidMount(){
       console.log(this.myRef.current);
    }
    

提示

React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值。ref 会在 componentDidMountcomponentDidUpdate 生命周期钩子触发前更新

Refs & 类组件 👻

使用Refs的时候,当 ref 属性用于自定义 class 组件时,ref 对象接收组件的实例作为其 current 属性。

import React from 'react'
class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef();
  }
  componentDidMount() {
    const node = this.myRef.current;
    console.log(node);
  }
  render() {
    return (
      <div>
        <p ref={this.myRef}>父亲</p>
      </div>
    )
  }
}
export default Parent

ref不仅可以直接定义在元素上,也可以定义在组件上

使用Refs的时候,当 ref 属性用于自定义 class 组件时,ref 对象接收组件的实例作为其 current 属性。

import React from 'react'
import Child from './child'
// import Sunzi from './sunzi'
class Parent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef();
    this.comRef = React.createRef();
  }
  componentDidMount() {
    const node = this.myRef.current;
    const comenode = this.comRef.current;
    console.log(node, comenode);
    this.comRef.current.onIndo()   //访问并调用到了类组件的方法
  }
  render() {
    return (
      <div>
        <p ref={this.myRef}>父亲</p>
        <Child ref={this.comRef} />
      </div>
    )
  }
}
export default Parent

回调 Refs & 类组件 👻

React 也支持另一种设置 refs 的方式,不同于传递 React.createRef() 创建的Refs,我们可以给ref属性传递一个函数。在这个函数中通过参数来获取 React 组件实例或 HTML DOM 元素。

import React from 'react'
import Child from './child'
class Parent extends React.Component {
  constructor(props) {
    super(props)
  }
  componentDidMount() {

  }
  inputDom
  setInput = (ele) => {
    console.log(ele);
    this.inputDom = ele
  }
  setcomRef = (ele) => {
    console.log(ele);
    this.inputDom = ele
    ele.onIndo()    //访问并调用到了类组件的方法
  }
  render() {
    return (
      <div>
        <input ref={this.setInput} />
        <Child ref={this.setcomRef} />
      </div>
    )
  }
}
export default Parent

高阶组件(HOC) 💎

高阶组件(HOC)是以组件为参数,返回值为新组件的函数。

可以把高阶组件看作是组件的加工厂,接收旧组件返回包装后的新组件。

什么情况下使用高阶组件

react如果有多个组件都用到了同一段逻辑, 这时,就可以把共同的逻辑部分提取出来,利用高阶组件的形式将这段逻辑整合到每一个组件中, 从而减少代码的逻辑重复

比如:我想在不同的组件中,分别获取用户信息,创建一个高阶组件

// /src/utils/hoc.tsx
import React from 'react'
const Hoc = (WrappedComponent) => {
    return class extends React.Component {
        constructor(props) {
            super(props)
            // 初始化state的userInfo属性
            this.state = {
                title:"标题",
                userInfo: {
                    name: "lcl"
                }
            }
        }
        componentDidMount() {
            // 获取用户信息,并且存储到state当中
            let userInfo = {
                name: "lcl"
            }
            this.setState({
                userInfo: userInfo
            })
        }
        render() {
            return (
                <WrappedComponent userInfo={this.state.userInfo} {...this.props} />
            )
        }
    }
}

export default Hoc

























 






import React from 'react'
import Child from './child'
import Hoc from "./utils/hoc"
const Parent = (props) => {
  return (
    <div>
      <p>父组件自身要获取用户信息</p>
      <p>你好:{props.userInfo.name}</p>
      <Child name='11111111111111' />
    </div>
  )
}
export default Hoc(Parent)








 




参数组件(被包装组件)除了接收需要从高阶组件返回的新组件(容器组件)获得的数据之外,还需要接收来自容器组件的所有 props,如代码中高亮的部分。

性能优化 💎

上次编辑于:
贡献者: 刘春龙
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.7