[React.js]ref和context

ref

用于获取真实Dom,和vue中的ref一个道理。

注意事项:

  1. ref必须在挂载后才能获取,通常在componentDidMount生命周期函数中
  2. ref获取组件只能获取类组件,不能获取函数组件。因为ref获取的是组件的实例,只有类组件有实例对象,函数组件并没有组件实例
  3. 创建方式有两种,createRefuseRef
  4. 在类组件中调用 createRef 创建ref,createRef 不接收任何参数。调用 createRef 声明的 ref 会在组件每次渲染的时候重新创建,每次渲染都会返回一个新的引用。
  5. 在函数组件中使用 useRef 创建 ref,接收一个任意初始值,如字符串、对象,甚至是函数等。使用 useRef 创建的 ref 只会在组件首次渲染时创建,每次都会返回相同的引用,所以通过 ref 可以拿到最新的值。

js

// App.js
import { Component, createRef } from 'react'
import Son from './son.js'
// 创建ref
let titleRef = createRef();
class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      msg: "hello App"
    }
    this.sonRef = createRef();
  }
  componentDidMount(){
    // 获取真实的Dom元素
    console.log(titleRef.current);
    // 获取类组件的组件实例对象
    console.log(this.sonRef);
  }
  render() {
    return (
      <>
        <h1 ref={titleRef}>{this.state.msg}</h1>
        <Son ref={this.sonRef}></Son>
      </>
    )
  }
}

export default App;

js

// son.js
import React from "react";
class Son extends React.Component {
    state = {
        mes: "son 组件"
    }
    render() {
        return (
            <div>
                {this.state.mes}
            </div>
        )
    }
}
export default Son;

<></>是空标签,类似于vue中的<template></template>,最后不会编译成真实的Dom元素。

context

context类似于Vue中的providerinject,用于嵌套很深的爷孙组件之间传值,需要逐层传递props或者state时。

但是在React中需要自己实现逻辑,将提供者放在一份单独的js文件,使用时导入这份js文件。要Context发挥作用,需要用到两种组件一个是Context生产者(Provider)通常是一个父节点,另外一个是Context的消费者通常是一个或者多个子节点,所以Context的使用基于生产者消费者模式

使用Context

js

// objCtx.js
import { createContext } from "react";
// 创建上下文组件
const objContext = createContext();
export default objContext;

通过React的createContext()创建一个Context上下文组件,这个Context组件包含两个组件,<Provider /><Consumer />

js

// App.js
import { Component } from 'react'
import Son from './son.js'
// 引入创建上下文组件的文件
import objContext from './objCtx.js'
class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      msg: "hello App",
      passMsg: "App组件传递的消息"
    }
  }
  render() {
    return (
      <>
        <h1>{this.state.msg}</h1>
        {/* 将Provider组件包裹住子组件 */}
        <objContext.Provider value={{ username: 'Gloria', info: this.state.passMsg }}>
          <Son></Son>
        </objContext.Provider>
      </>
    )
  }
}

export default App;

Context.Provider包裹住一个组件后,其所有的子孙组件都可以获取到指定上下文的值。Provider(生产者),用于生产共享数据的地方。Provider只接收一个 value 属性,属性值就是需要传递给消费组件的数据。Context.Provider组件可以有多个也是可以多层嵌套的,Context.Provider再包裹另一个Context.Provider

js

// son.js
import React from "react";
import GrandSon from './grandSon.js'
// 引入创建上下文组件的文件
import objContext from "./objCtx.js";
class Son extends React.Component {
    state = {
        mes: "App的子组件"
    }
    render() {
        return (
            <>
                <h4>
                    {this.state.mes}
                </h4>
                {/* Consumer组件获取数据 */}
                <objContext.Consumer>
                    {
                        (ctxValue) => {
                            console.log(ctxValue,"son组件接收provider生产者传递的数据");
                            return (
                                <>
                                    <p>{ctxValue.username}</p>
                                </>
                            )
                        }
                    }
                </objContext.Consumer>
                <GrandSon></GrandSon>
            </>
        )
    }
}
export default Son;

js

// grandSon.js
import { Component } from 'react'
// 引入创建上下文组件的文件
import objContext from "./objCtx.js";
export default class GrandSon extends Component {
    state = {
        msg: "App的孙子组件"
    }
    render() {
        return (
            <>
                <h6>{this.state.msg}</h6>
                {/* Consumer组件获取数据 */}
                <objContext.Consumer>
                    {
                        (ctxValue) => {
                            console.log(ctxValue, "grandson组件接收provider生产者传递的数据");
                            return (
                                <>
                                    <p>{ctxValue.info}</p>
                                </>
                            )
                        }
                    }
                </objContext.Consumer>
            </>

        )
    }
}

Context.Consumer组件是消费者,该组件中消费者必须使用函数接收当前的context值,然后返回一个 React 节点。传递给函数的value值就是Provider提供的value值。

获取context的地方

可以直接获取Context的地方,除了实例的context属性(this.context),React组件还有很多个地方可以直接访问父组件提供的Context。

类组件:

  • constructor(props, context)
  • componentWillReceiveProps(nextProps, nextContext)
  • shouldComponentUpdate(nextProps, nextState, nextContext)
  • componetWillUpdate(nextProps, nextState, nextContext)
  • 所有能访问this的地方都可以使用this.context来获取上下文对象,就可以得到提供者的数据

如果想要用this.context来获取上下文对象必须在接受值的后代组件名.contextType = 引入的上下文对象(即在后代消费者组件导出时给组件添加一个contextType属性,属性值就是引入的上下文对象),这样才能在消费者组件中用this.context来获取上下文对象,否则是个空对象。

this.context获取空对象

provider数据生产者

js

// App.js
import { Component } from 'react'
import Son from './son.js'
// 引入创建上下文组件的文件
import objContext from './objCtx.js'
class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      msg: "hello App",
      passMsg: "App组件传递的消息"
    }
  }
  render() {
    return (
      <>
        <h1>{this.state.msg}</h1>
        {/* 将Provider组件包裹住子组件 */}
        <objContext.Provider value={{ username: 'Gloria', info: this.state.passMsg }}>
          <Son></Son>
        </objContext.Provider>
      </>
    )
  }
}

export default App;

consumer数据消费者

js

import React from "react";
import GrandSon from './grandSon.js'
// 引入创建上下文组件的文件
import objContext from "./objCtx.js";
class Son extends React.Component {
    state = {
        mes: "App的子组件"
    }
    print = () => {
        console.log(this.context, 666666);
    }
    render() {
        return (
            <>
                <h4>
                    {this.state.mes}
                </h4>
                <button onClick={this.print}>print ctx son组价</button>
                {/* Consumer组件获取数据 */}
                <objContext.Consumer>
                    {
                        (ctxValue) => {
                            console.log(ctxValue, "son组件接收provider生产者传递的数据");
                            return (
                                <>
                                    <p>{ctxValue.username}</p>
                                </>
                            )
                        }
                    }
                </objContext.Consumer>
                <GrandSon></GrandSon>
            </>
        )
    }
}
export default Son;

consumer数据消费者

js

// grandSon.js
import { Component } from 'react'
// 引入创建上下文组件的文件
import objContext from "./objCtx.js";
export default class GrandSon extends Component {
    state = {
        msg: "App的孙子组件"
    }
    printctx = () => {
        console.log(this.context, 777777);
    }
    render() {
        return (
            <>
                <h6>{this.state.msg}</h6>
                <button onClick={this.printctx}>print context grandson组件</button>
                {/* Consumer组件获取数据 */}
                <objContext.Consumer>
                    {
                        (ctxValue) => {
                            console.log(ctxValue, "grandson组件接收provider生产者传递的数据");
                            return (
                                <>
                                    <p>{ctxValue.info}</p>
                                </>
                            )
                        }
                    }
                </objContext.Consumer>
            </>

        )
    }
}

this.context正确获取方法

provider数据生产者

js

// App.js
import { Component } from 'react'
import Son from './son.js'
// 引入创建上下文组件的文件
import objContext from './objCtx.js'
class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      msg: "hello App",
      passMsg: "App组件传递的消息"
    }
  }
  render() {
    return (
      <>
        <h1>{this.state.msg}</h1>
        {/* 将Provider组件包裹住子组件 */}
        <objContext.Provider value={{ username: 'Gloria', info: this.state.passMsg }}>
          <Son></Son>
        </objContext.Provider>
      </>
    )
  }
}

export default App;

consumer数据消费者

js

import React from "react";
import GrandSon from './grandSon.js'
// 引入创建上下文组件的文件
import objContext from "./objCtx.js";
class Son extends React.Component {
    state = {
        mes: "App的子组件"
    }
    print = () => {
        console.log(this.context, 666666);
    }
    render() {
        return (
            <>
                <h4>
                    {this.state.mes}
                </h4>
                <button onClick={this.print}>print ctx son组价</button>
                {/* Consumer组件获取数据 */}
                <objContext.Consumer>
                    {
                        (ctxValue) => {
                            console.log(ctxValue, "son组件接收provider生产者传递的数据");
                            return (
                                <>
                                    <p>{ctxValue.username}</p>
                                </>
                            )
                        }
                    }
                </objContext.Consumer>
                <GrandSon></GrandSon>
            </>
        )
    }
}
// 后代消费者组件导出时给组件添加一个contextType属性,属性值就是引入的上下文对象
Son.contextType = objContext;
export default Son;

consumer数据消费者

js

// grandSon.js
import { Component } from 'react'
// 引入创建上下文组件的文件
import objContext from "./objCtx.js";
export default class GrandSon extends Component {
    state = {
        msg: "App的孙子组件"
    }
    printctx = () => {
        console.log(this.context, 777777);
    }
    render() {
        return (
            <>
                <h6>{this.state.msg}</h6>
                <button onClick={this.printctx}>print context grandson组件</button>
                {/* Consumer组件获取数据 */}
                <objContext.Consumer>
                    {
                        (ctxValue) => {
                            console.log(ctxValue, "grandson组件接收provider生产者传递的数据");
                            return (
                                <>
                                    <p>{ctxValue.info}</p>
                                </>
                            )
                        }
                    }
                </objContext.Consumer>
            </>

        )
    }
}
// 后代消费者组件导出时给组件添加一个contextType属性,属性值就是引入的上下文对象
GrandSon.contextType = objContext;

标签

发表评论