React实现跨组件通讯

以前可以使用一层一层的往下传递,这种可以解决需求,

1.父组件往孙子组件传值:

App.js

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

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      nikename:"coderhzc",
      level:88
    }
  }
  render() {
    const {nikename,level} = this.state
    return (
      <div>
        <Profile nikename={nikename} level={level}/>
      </div>
    )
  }
}

profile.js文件(注意是函数组件)

import React from 'react';
import ProfileHeader from './ProfileHeader';

export default function Profile(props) {
  // 从父组件传递过来的
  console.log(props,"profile");
  return (
    <div>
      <ProfileHeader nikename={props.nikename} level={props.level}/>
      <ul>
        <li>设置1</li>
        <li>设置2</li>
        <li>设置3</li>
        <li>设置4</li>
      </ul>
    </div>
  )
}

ProfileHeader.js 是profile.js 的子文件 (注意是函数组件)

import React from "react";

export default function ProfileHeader(props) {
  console.log(props,"ProfileHeaderProfileHeaderProfileHeader");
  return (
    <div>
      <h2>用户昵称: {props.nikename}</h2>
      <h2>用户等級: {props.level}</h2>
    </div>
  );
}

实际截图

image.png

** 以上可以实现但是非父子组件就搞不定了 **

补充以上缺陷使用属性展开 {...props},就可以省略Profile.js中的那段多余的传值

image.png

2. 跨组件通讯Context应用场景

Context相关API

(1) React.createContext
 --创建一个需要共享的Context对象:
-- 如果一个组件订阅了Context,那么这个组件会从离自身最近的那个匹配的 Provider 中读取到当前的context值;
 -- defaultValue是组件在顶层查找过程中没有找到对应的Provider,那么就使用默认值
const myContext = React.create


(2) Context.Provider
-- 每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化:
-- Provider 接收一个 value 属性,传递给消费组件;
-- 一个 Provider 可以和多个消费组件有对应关系;
-- 多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据;
-- 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染;
<MyContext.Provider value={/*某个值*/}>

(3) Class.contextType
-- 挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象:
-- 这能让你使用 this.context 来消费最近 Context 上的那个值; p 你可以在任何生命周期中访问到它,包括 render 函数中;
MyClass.contextType = MyContext

(4) Context.Consumer
-- 这里,React 组件也可以订阅到 context 变更。这能让你在 函数式组件 中完成订阅 context。 
-- 这里需要 函数作为子元素(function as child)这种做法; 
-- 这个函数接收当前的 context 值,返回一个 React 节点;
<MyContext.Consumer>
  {value => /*基于context值进行渲染*/}
</MyContext.Consumer>

(4.1) Class 类组件的具体使用代码如下:

import React, { Component } from "react";

// 1.创建Context对象
// defaultValue是组件在顶层查找过程中没有找到对应的Provider,那么就使用默认值
// const UserContext = React.createContext(defaultValue)
export const UserContext = React.createContext({
  nikename: "aaaaa",
  level: -100,
});


// 组件
// 3. 具体的使用方式,记住不能使用函数组件
class ProfileHeader extends Component {
  render() {
    console.log(this.context);
    return (
      <div>
        <h2>用户昵称: {this.context.nikename}</h2>
        <h2>用户等級: {this.context.level}</h2>
      </div>
    );
  }
}
// 4. 把创建的Context对象赋值给ProfileHeader的contextType就可以在孙子组件中拿到从爷爷组件传递的数据了
ProfileHeader.contextType = UserContext;

function Profile(props) {
  return (
    <div>
      <ProfileHeader />
      <ul>
        <li>设置1</li>
        <li>设置2</li>
        <li>设置3</li>
        <li>设置4</li>
      </ul>
    </div>
  )
}


export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      objName: {
        nikename: "coderhzc",
        level: 88,
      },
    };
  }
  render() {
    return (
      <div>
        {/* 2.具体使用 */}
        {/* 
           (1) Provider 接收一个 value 属性,传递给消费组件;
           (2) 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染;
         */}
        <UserContext.Provider value={this.state.objName}>
          <Profile />
        </UserContext.Provider>
      </div>
    );
  }
}

实际截图

image.png

### (4.2) 函数组件的具体使用代码如下:

import React, { Component } from "react";

// 1.创建Context对象
// defaultValue是组件在顶层查找过程中没有找到对应的Provider,那么就使用默认值
// const UserContext = React.createContext(defaultValue)
const UserContext = React.createContext({
  nikename: "aaaaa",
  level: -100,
});

// 组件
// 3. 函数组件的具体使用
function ProfileHeader() {
  return (
    <div>
      <UserContext.Consumer>
        {
         value => {
          console.log(value);
          return (
            <div>
              <h2>用户昵称: {value.nikename}</h2>
              <h2>用户等級: {value.level}</h2>
            </div>
          );
        }
        }
      </UserContext.Consumer>
    </div>
  );
}

function Profile(props) {
  return (
    <div>
      <ProfileHeader />
      <ul>
        <li>设置1</li>
        <li>设置2</li>
        <li>设置3</li>
        <li>设置4</li>
      </ul>
    </div>
  );
}

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      objName: {
        nikename: "coderhzc11",
        level: 88,
      },
    };
  }
  render() {
    return (
      <div>
        {/* 2.具体使用 */}
        {/* 
           (1) Provider 接收一个 value 属性,传递给消费组件;
           (2) 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染;
         */}
        <UserContext.Provider value={this.state.objName}>
          <Profile />
        </UserContext.Provider>
      </div>
    );
  }
}

实际截图

image.png

5. 多个Context使用代码如下(不推荐做):

import React, { Component } from 'react';

// 创建Context对象
const UserContext = React.createContext({
  nickname: "aaaa",
  level: -1
})

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

function ProfileHeader() {
  // jsx -> 嵌套的方式
  return (
    <UserContext.Consumer>
      {
        value => {
          return (
            <ThemeContext.Consumer>
              {
                theme => {
                  return (
                    <div>
                      <h2 style={{color: theme.color}}>用户昵称: {value.nickname}</h2>
                      <h2>用户等级: {value.level}</h2>
                      <h2>颜色: {theme.color}</h2>
                    </div>
                  )
                }
              }
            </ThemeContext.Consumer>
          )
        }
      }
    </UserContext.Consumer>
  )
}

function Profile(props) {
  return (
    <div>
      <ProfileHeader />
      <ul>
        <li>设置1</li>
        <li>设置2</li>
        <li>设置3</li>
        <li>设置4</li>
      </ul>
    </div>
  )
}

export default class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      nickname: "kobe",
      level: 99
    }
  }

  render() {
    return (
      <div>
        <UserContext.Provider value={this.state}>
          <ThemeContext.Provider value={{ color: "red" }}>
            <Profile />
          </ThemeContext.Provider>
        </UserContext.Provider>
      </div>
    )
  }
}

实际截图

image.png

终极写法:组件抽离的写法

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容