[React.js]ref和context
ref
用于获取真实Dom,和vue中的ref一个道理。
注意事项:
- ref必须在挂载后才能获取,通常在
componentDidMount
生命周期函数中 - ref获取组件只能获取类组件,不能获取函数组件。因为ref获取的是组件的实例,只有类组件有实例对象,函数组件并没有组件实例
- 创建方式有两种,
createRef
和useRef
。 - 在类组件中调用
createRef
创建ref,createRef
不接收任何参数。调用createRef
声明的 ref 会在组件每次渲染的时候重新创建,每次渲染都会返回一个新的引用。 - 在函数组件中使用
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中的provider
和inject
,用于嵌套很深的爷孙组件之间传值,需要逐层传递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.contex
t),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;
发表评论