React context使用方法

常见的数据流动

  • state: 数据在组件内流动
    在这里插入图片描述
  • props: 数据在父子组件间流动
    在这里插入图片描述
  • redux:数据在单一数据源和组件间流动
    在这里插入图片描述
  • context:数据在Context和使用context的组件间流动 || Provider和使用context的组件间流动
    在这里插入图片描述

context和redux的区别

  • context的初始数据( React.createContext() )不能变更
    • redux由于是单一数据源,可通过dispatch改变数据
  • context数据不稳定,取决于最近的Provider
    • 数据稳定,单一数据源,单一修改方法
  • 使用context的组件需import 对应的Context对象进来
    • 使用redux的组件需connect 连接对应的数据源

context解决的问题

  • 用于跨组件通信
  • 可以理解为简略版的redux
    在这里插入图片描述

context存在的问题

  • 由于context的值主要取决于最近的Provider,如果没有匹配的Provider就会使用默认值
    • 假如有个嵌套100层的组件结构,你在第1层使用UserContext.Provider指定了数据,第33、66、99层使用了context的值
    • 一年后,你同事在第40层加了个UserContext.Provider指定了数据,在60、80、100层使用了context的值
    • 这时你33、66、99层的context会去第40层的Provicer中去找,因为它比较近,如果Provider的数据或结构不一样,就容易产生异常

在这里插入图片描述

注意点

  • 创建Provider时,注意上层有没有相同的Provider,不要覆盖了原来的逻辑
  • 合理设置Provider的嵌套关系
  • 建议设置Provider的displayName,可以在React DevTools中快速了解Provider的嵌套情况

适用场景

  • 深色主题 / 浅色主题 切换
  • 多语言的切换
  • 一些类似开关的场景

demo

// context.js - 一个专门用来保存cotext的js文件
import React from 'react';

/**
 * 全局用户信息
 * @param {*} [defaultValue] - 默认值,没有匹配到 Provider生效
 * */
export const UserContext = React.createContext({
  username: 'admin',
  password: '123456'
});
UserContext.displayName = 'UserContext';

/**
 * 全局默认主题
 * */
export const ThemeContext = React.createContext({
  backgroundColor: '#FFF',
  fontSize: '16px'
});
import React from 'react';
import { Layout } from 'antd';

import Header from './header';
import Content from './content';
import Footer from './footer';

import { UserContext } from './context';

export default class Home extends React.Component{
  
  render() {
      return (
	      <Layout>
	        {/**
		          Provider用于覆盖默认值
		          Provider包裹的组件不会使用默认值,只会使用Provider上的value的值
		          value没有指定,这时context的值是undefined
		
		          context = {username: 'giegie'}
	         */}
	        <UserContext.Provider value={{ username: 'giegie' }}>
	          <Header />
	        </UserContext.Provider>
	
			{/**
	            外部没有对应的Provider,使用默认数据
	
	            context = { username: 'admin', password: '123456' }
         	*/}
	        <Content />
	
	        {/**
		           Provider用于覆盖默认值
		           Provider包裹的组件不会使用默认值,只会使用Provider上的value的值
		           value没有指定,这时context的值是undefined
		
		           context = undefined
	         */}
	        <UserContext.Provider>
	          <Footer />
	        </UserContext.Provider>
	      </Layout>
    );
  }
};
import React from 'react';
import { UserContext } from './context';

export default class Header extends React.Component {

  /**
   * 根据名字XXX 找到最近的 XXX.Provider
   *
   * e.g.
   * this.context = UserContext 找到上层最近的 <UserContext.Provider>
   * Provider包裹的组件不会使用默认值,只会使用Provider上的value的值
   * value没有指定,这时context的值是undefined
   *
   * 可以在任何地方通过this.context 来使用 context的值
   *
   * 用contextType只能绑定一个context,需要使用多个context用Consumer
   * */
  static contextType = UserContext;

  render() {
    return (
      <>
        <h1>header</h1>
        <h2>user: {this.context?.username}</h2>

        {/**
             一个订阅context变化的组件
             子元素为函数

             @param {*} value - context的值
             @return          - 返回一个React节点
         */}
        <UserContext.Consumer>
          {
            value => <h2>user: {value.username}</h2>
          }
        </UserContext.Consumer>
      </>
    )
  }
}
// 函数组件使用 - Consumer - 整个组件消费context
import React from 'react';
import { UserContext } from './context';

export default function Content(props) {
  
  return (
    /**
     *  一个订阅context变化的组件
     *
     *  子元素为函数
     *
     *  @param {*} value - context的值
     *  @return          - 返回一个React节点
     *  */
    <UserContext.Consumer>
      {
        value => (
          <>
            <h1>content</h1>
            <h2>user: {value.username}</h2>
          </>
        )
      }
    </UserContext.Consumer>
  );
};


// 函数组件使用 - Consumer - 局部消费context
import { UserContext } from './context';

export default function Content(props) {

  return (
    <>
      <h1>content</h1>]

      <UserContext.Consumer>
        { value => <h2>user: {value.username}</h2> }
      </UserContext.Consumer>

      <UserContext.Consumer>
        { value => <h2>password: {value.password}</h2> }
      </UserContext.Consumer>
    </>
  );
};


// 函数组件使用 - useContext - 函数任意地方使用
import React, { useContext } from 'react';
import { UserContext } from './context';

export default function Content(props) {
  const userContext = useContext(UserContext);

  return (
    <>
      <h1>content</h1>

      <h2>user: {userContext.username}</h2>
      <h2>password: {userContext.password}</h2>
    </>
  );
};

使用多个context

import React from 'react';
import { UserContext, ThemeContext } from './context';

// 类组件或函数组件,通过多个不同的Consumer使用
export default class Header extends React.Component {

  render() {
    return (
      <>
        <h1>header</h1>
        <h2>user: {this.context?.username}</h2>

        <ThemeContext.Consumer>
          {
            value => <h2>fontSize: {value.fontSize}</h2>
          }
        </ThemeContext.Consumer>

        <UserContext.Consumer>
          {
            value => <h2>user: {value.username}</h2>
          }
        </UserContext.Consumer>
      </>
    )
  }
}

==========

// 函数组件使用 - useContext - 函数任意地方使用
import React, { useContext } from 'react';
import { UserContext, ThemeContext } from './context';

export default function Content(props) {
  const userContext = useContext(UserContext);
  const themeContext = useContext(ThemeContext);

  return (
    <>
      <h1>content</h1>

      <h2>user: {userContext.username}</h2>
      <h2>password: {userContext.password}</h2>
      
	  <h2>fontSize: {themeContext.fontSize}</h2>
    </>
  );
};

context造成的页面渲染

  /**
   * 组件发生重渲染的情况
   *    - 组件内部的state、props、context发生变化组件就会重渲染
   *    - 父组件重渲染,子组件也会重渲染
   *
   *
   * 注意点
   *    - context改变不会执行shouldComponentUpdate
   *    - 通过static contextType使用的context,只要context发生变化,整个组件会重渲染
   *    - 通过XXX.Consumer使用的context,只要context发生变化,Consumer内部会重渲染,不会引起整个组件的重渲染
   *    - 通过useCallback使用的context,只要context发生变化,整个函数组件内部会重新执行
   * */

displayName

设置displayName可以在React DevTools中,更容易被找到

import React from 'react';

export const UserContext = React.createContext({
  ...
});
UserContext.displayName = 'UserContext';

export const ThemeContext = React.createContext({
  ...
});
ThemeContext.displayName = 'ThemeContext';

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值