👨‍💻
Hong's 前端笔记
  • 🌈About
  • JavaScript
    • 作用域
    • 闭包
    • this
      • bind()
    • 原型
      • Object.create()
      • 模拟“类”
      • ES6 Class
      • Function
    • 对象
    • 类型 & 值
      • 原生函数
      • 数组
      • 字符串
    • 异步
    • JavaScript 相关概念
      • Event Loop 事件循环
      • Prototype 原型
      • Context 执行上下文
      • this
      • Promise
    • JavaScript 常见问题
      • 手写实现
      • 看代码输出
  • React
    • 核心概念
    • 深入理解
      • State
      • 如何管理 State
      • React 渲染过程
      • 处理 DOM 事件
      • Pure 组件
      • Context
      • Key 属性
      • React.lazy()
      • Render Props
    • Hooks
      • useState
      • useRef
      • useEffect
        • Some details
      • 相关概念
      • 自定义 Hook
      • useEffect vs useLayoutEffect
    • React 相关问题
      • Class vs Function
  • React Libraries
    • Redux
      • Quick Start
      • 如何持久化 Redux 数据
      • 异步操作
      • 性能优化
      • Dive into immutability
    • Route v6
      • Quick Start
  • Reactivity
    • Immer
  • Vue
    • 深入理解
      • Composition vs Options
      • Reactivity
  • Network
    • Cookies
    • HTTP
    • HTTPS
    • CORS 跨域资源共享
    • 认证手段
    • 安全相关
    • 网络相关知识
  • Browser
    • DOM 操作
    • Events 事件
    • XHR & Fetch
    • 性能优化
    • HTML 相关概念
    • 浏览器相关概念
  • CSS
    • 盒模型
    • Layout 布局
    • Styles 样式
    • CSS 相关概念
    • CSS 相关技巧
      • 移动端适配
      • Flex
      • 动画
  • TypeScript
    • Quick Peek
    • Types in TypeScript
    • Narrowing
    • Functions in TypeScript
  • Workflow
    • Webpack
      • Webpack 概念
      • 资源管理
      • 管理输出
      • 开发环境配置
      • 生产环境配置
      • 优化代码运行性能
  • Others
    • 小程序与原生 Web 的区别
  • SSO
    • About
    • API Doc
    • 接入指南
Powered by GitBook
On this page
  • Intro
  • createContext(defaultValue)
  • Class 组件消费 context 提供的值
  • Context.Provider
  • Context.Consumer
  • useContext()
Edit on GitHub
  1. React
  2. 深入理解

Context

Intro

context 可以在多个层级的组件中避免通过 props 单级的层层传递数据。

什么时候需要使用 context ?

当一个组件树需要共享一组全局数据时,如主题、首选语言或当前认证的用户。

但是要避免不要过度滥用 context,他会破坏组件的可复用性。

context 更适合**为每一层组件都提供数据**,而不是针对跨组件(子孙组件需要访问父级组件)进行数据传输。

createContext(defaultValue)

首先需要创建一个 context,createContext() 接受一个”默认值”参数,来保证在找不到 Provider 的时候,可以使用该默认值。

// context.js
import React from "react"

const ThemeColorContext = React.createContext('blue')

export {ThemeColorContext}

defaultValue

defaultValue 是最后的 fallback 选项,是静态的、永远不会修改的。

Context 对象

createContext() 返回的是一个 context 对象。Context 对象本身不持有任何信息,它通过 Context.Provider,Context.Consumer 属性来实现新的提供和消费,或者使用 useConext() hook 来消费。

Class 组件消费 context 提供的值

如果是类组件,使用 Class.contextType 来指定某一组件来消费 context 提供的值

import React from "react";
import { ThemeContext } from "../../module/context";

class Tag extends React.Component {
    render() { 
        return (
            <div style={{backgroundColor: this.context, color: '#fff', padding: '10px', borderRadius: '8px'}}>{this.props.children}</div>
        )
    }
}
// 在类的外部指定 contextType,在类的内部可以通过 this.context 来消费
Tag.contextType = ThemeColorContext 
export default Tag;

或者使用 static 关键字在类的内部指定:

class Tag extends React.Component {
    // 使用 static 来指定类的静态成员
    static contextType = ThemeColorContext
    render() { 
        return (
            <div style={{backgroundColor: this.context, color: '#fff', padding: '10px', borderRadius: '8px'}}>{this.props.children}</div>
        )
    }
}

Context.Provider

由 createContext 创建的 Context 对象,包含一个 Context.Provider 组件。

被这个组件包裹的其他对象,都可以消费由 provider 指定的 context 值。使用 value 属性来指定 context 值。

例如上面的例子,如果不提供一个 Provider,Tag 组件会使用默认的颜色,即蓝色。

而如果用一个 Provider 包裹住,Tag 会消费由 Provider 提供的值。

import Tag from './components/tag'
import './App.css'
import { ThemeColorContext } from './module/context'

export default function App() {
    return (
        <div className="container">
            <Tag>Hello World</Tag>

            <ThemeColorContext.Provider value={'red'}>
                <Tag>Hello World</Tag>
            </ThemeColorContext.Provider>
        </div>
    )
}

触发重渲染

每当 Provider 的 value 属性发生变更的时候,组件就会触发渲染。

监听 value 变化使用的是浅比较,而不比较具体的内容。因此,有一种情况会触发不必要的重渲染:即 Provider 组件发生渲染后,提供的 value 对象也发生了变化(创建了新的对象),从而触发了重渲染:

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: 'something'}}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}

可以使用 state 来保存 value 值,从而保证不会触发不必要的重渲染。

多个 Provider 嵌套

Provider 可能不止一个,多个 Provider 可以一层层叠加

<ThemeContext.Provider value={theme}>
    <UserContext.Provider value={signedInUser}>
        ...
    </UserContext.Provider>
</ThemeContext.Provider>

Context.Consumer

Context.Consumer 利用 render prop。向它的 children 传入一个渲染函数,这个渲染函数接受一个 context 值,函数内部根据 context 的具体内容来返回一个 React 元素。

context 是自底向上根据最近的 Provider 决定的,如果找不到,则使用 context 的默认值。

<MyContext.Consumer>
  {context => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>

新的 useContext() 实现了类似的功能,更应该考虑使用它。

useContext()

使用方法

const value = useContext(SomeContext)
import { useContext } from "react";
import { ThemeColorContext } from "../../module/context";

function Tag({children = ''}) {
    const themeColor = useContext(ThemeColorContext)

    return ( 
        <div style={{backgroundColor: themeColor, color: '#fff', padding: '10px', borderRadius: '8px'}}>{children}</div>
     )
}

export default Tag;

Last updated 3 years ago

image-20220430180817949