React学习笔记[9]✨~一文掌握Context👻

66 阅读4分钟

我正在参加「掘金·启航计划」

一、Context的应用场景

React中的Context主要用于非父子组件之间数据的共享

在开发中,对于一些场景的数据需要在多个组件之间进行共享(如:用户信息、UI主题、地区偏好等等)

如果将这些信息定义在了顶层组件App中,然后通过props一层层传递下去,这样的话对于中间一些使用不到这些数据的组件来说无非是一种冗余的操作。

针对这种场景,React提供了Context,Context的设计目的是为了共享那些对于一个组件树而言是“全局”的数据。Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。

二、类组件中使用共享的数据

  1. 首先通过 React.createContext 创建一个用来共享数据的Context对象

为了方便在组件中使用Context对象提供的Provider组件与Consumer组件,可将创建Context对象的逻辑写在专门的js文件中,并将创建好的Context对象导出

比如,创建一个共享主题UI数据的Context对象:

theme-context.js:

import React from "react"
const ThemeContext = React.createContext()

export default ThemeContext
  1. 使用Context对象提供的Provider组件去包裹子组件

想要被共享数据的子组件必须被Provider包裹、共享的数据通过 value={…} 来传递

App.jsx:

import React, { Component } from 'react'
import Home from './Home'
import ThemeContext from './context/theme-context'
export class App extends Component {
  render() {
    return (
      <div>
        <ThemeContext.Provider value={{ color: 'red'}}>
          <Home />         
        </ThemeContext.Provider>
      </div>
    )
  }
}

export default App
  1. 在类组件中使用Context对象中共享的数据

类组件的实例中有context属性,但是其值默认是{}

由于Context的Provider 是可以嵌套的,所以要在子组件中通过 类.contextType=创建好的Context 来指定好要获取哪个上下文中的数据; 指定好后便可以通过this.context来获取到Context对象中共享的数据了

Home.jsx:

import React, { Component } from 'react'
import HomeInfo from './HomeInfo'

export class Home extends Component {
  render() {

    return (
      <div>
        <HomeInfo/>
      </div>
    )
  }
}

export default Home

HomeInfo.jsx:

import React, { Component } from 'react'
import ThemeContext from './context/theme-context'

export class HomeInfo extends Component {
  render() {
    // 2. 此时便可以通过实例.context获取到共享的数据
    console.log('context', this.context) // {color: 'red'}
    return (
      <div style={{ color: this.context.color }}>HomeInfo</div>
    )
  }
}

// 1. 指定想要使用的Context对象(想从哪个上下文中获取共享的数据)
HomeInfo.contextType = ThemeContext

export default HomeInfo

三、函数组件中使用共享的数据

函数组件中是没有 this.context 的,在函数组件中如果也想使用Context对象提供的共享数据,可使用 Context对象提供的 Consumer 组件来使用共享数据

在Consumer中传递一个回调函数,这个回调函数就是Consumer 的children,其内部会回调该函数并将共享的数据以参数的形式传递过来

import ThemeContext from './context/theme-context'
function HomeInfo() {
  return <div>
    <ThemeContext.Consumer>
      {
        value => {
          return <div style={{ color: value.color }}>HomeInfo</div>
        }
      }
    </ThemeContext.Consumer>
  </div>
}

export default HomeInfo

四、使用多个Context共享的数据

在React中组件是可以使用多个Context提供的共享数据的:

多个Context.Provider是可以嵌套的

1. 首先,要将多个上下文对象的Provider嵌套起来包裹想要获取共享数据的子组件

App.jsx:

import React, { Component } from 'react'
import Home from './Home'
import ThemeContext from './context/theme-context'
import UserContext from './context/user-context'

export class App extends Component {
  render() {
    return (
      <div>
        <ThemeContext.Provider value={{ color: 'red'}}>
          <UserContext.Provider value={{ name: 'zs'}}>
            <Home />
          </UserContext.Provider>
        </ThemeContext.Provider>
      </div>
    )
  }
}

export default App

2. 在函数组件中可通过使用多个上下文的Consumer来实现获取多个上下文对象中的共享数据

import ThemeContext from './context/theme-context'
import UserContext from './context/user-context'
function HomeInfo() {
  return <div>
    <ThemeContext.Consumer>
      {
        value => {
          return <div style={{ color: value.color }}>
            <UserContext.Consumer>
              {
                userInfo => {
                  return <span>NAME: {userInfo.name}</span>
                }
              }
            </UserContext.Consumer>
          </div>
        }
      }
    </ThemeContext.Consumer>
  </div>
}

export default HomeInfo

需要注意的是:

由于类组件中只能通过 类.contextType = xxx 来指定获取哪一个上下文中共享的数据,所以如果想在类组件中使用多个上下文中共享的数据的话也可以使用Context对象提供的Consumer组件

import React, { Component } from 'react'
import ThemeContext from './context/theme-context'
import UserContext from './context/user-context'

export class HomeInfo extends Component {
  render() {
    return (
      <div>
        <h2>HomeInfo: {this.context.color}</h2>
        <UserContext.Consumer>
          {
            userInfo => {
              return <h2>Info User: {userInfo.name}</h2>
            }
          }
        </UserContext.Consumer>
      </div>
    )
  }
}

// 3.第三步操作: 设置组件的contextType为某一个Context
HomeInfo.contextType = ThemeContext

export default HomeInfo

五、Context默认值的用法

当某个组件想使用某个上下文中共享的数据,但该组件又没有被这个Context对象的Provider包裹时,此时该组件中使用的共享数据将是创建上下文时所传递的默认值

在创建上下文对象的时候是可以传递默认值的:

import React from "react"

const ThemeContext = React.createContext({ color: "blue" })

export default ThemeContext

比如在子组件Profile没有被 ThemeContext的Provider包裹:

import React, { Component } from 'react'
import Home from './Home'
import ThemeContext from './context/theme-context'
import UserContext from './context/user-context'
import Profile from './Profile'

export class App extends Component {
  render() {
    return (
      <div>
        <ThemeContext.Provider value={{ color: 'red'}}>
          <UserContext.Provider value={{ name: 'zs'}}>
            <Home />
          </UserContext.Provider>
        </ThemeContext.Provider>
        <Profile />
      </div>
    )
  }
}
export default App

此时,在Profile中使用ThemeContext中提供的数据时,获取到的为创建ThemeContext时传递的默认数据({ color: blue }):

import React, { Component } from 'react'
import ThemeContext from './context/theme-context'

export class Profile extends Component {
  render() {
    console.log(this.context)

    return (
      <div>Profile</div>
    )
  }
}

Profile.contextType = ThemeContext

export default Profile