React-Native Context跨层级的组件通信

场景

如图:APP界面嵌套A嵌套B嵌套C嵌套D嵌套E嵌套F......

场景一:从APP界面获取数据,需要F来显示,怎么把APP界面的值传递给F?使用props一层一层的逐级向下传递么😢

场景二:最底层F需要触发一个事件onClick,需要APP界面来接受参数并进行下一步的操作,怎么在F中触发onClick然后APP界面也响应进行处理?使用props{callBack=>{}}逐级向上传递么😢

answer:NO

API

赶快使用 Context 吧~

let { Provider, Consumer } = React.createContext()

创建一对{ Provider, Consumer }。当 React 渲染 context 组件 Consumer 时,它将从组件树的上层中最接近的匹配的 Provider 读取当前的 context 值。

React.createContext(defaultValue) 可以指定默认值,当Provider没有value时,Consumer中取的就是defaultValue;当Provider存在value时,会把defaultValue进行覆盖,Consumer中取的就是Providervalue

  • ProviderConsumer 是 一一对应滴~ BConsumer是取不到AProvider中的value滴~
  • Provider 组件的 value 值发生变更时,其内部组件树中对应的 Consumer 组件会接收到新值并重新执行 内部 函数。此过程不受 shouldComponentUpdete 方法的影响。
  • Provider组件利用Object.is检测 value 的值是否有更新。注意 Object.is=== 的行为不完全相同呀

Provider

<Provider value={/* some value */}>
  ...
</Provider>
接收一个 value 属性传递给Provider 的后代 Consumers。一个 Provider 可以链接到多个 ConsumersProviders 可以被嵌套以覆盖组件树内更深层次的值。
Provider 包裹的组价内部 可以通过Consumers访问到 Providervalue

⚠️⚠️⚠️注意:尽量不要在这里 <Provider value={/* some value */}>value 赋具体的值,比如 <Provider value={{key: 'value',key1: 'value1'...}}
因为只要render执行一次,Providervalue就会返回一个全新的{},不管里面的值是否改变,与Provider对应的Consumer都会重新执行一次。为了减少不必要的刷新,尽量使用state,然后配合PureComponent进行性能优化

Consumer

<Consumer>
  {
      value => {
          coding......
      }
  }
</Consumer>
注意 Consumer 内部是一个方法,有一个value。这个参数就是 Providervalue。得到value,可以在方法里面进行相应的操作,返回组件或者存值都可以
image.png

image.png

使用

context.js

import React from "react";

const AppContext = React.createContext()
const AContext = React.createContext()
const BContext = React.createContext()


export {
    AppContext,
    AContext,
    BContext
}

APP界面

this.state = {
   title: '123',
}
render() {
    return (
        <View>
            <TouchableOpacity style={{marginTop: 50, width: 100 ,height: 44, backgroundColor: 'white',justifyContent: 'center',alignItems: 'center'}}
                              onPress={()=>{

                                  // 点击按钮修改数据
                                  this.setState({
                                      title: '456'
                                  })
                              }}>
                <Text>点击改变数据</Text>
            </TouchableOpacity>
            
            //Provider  包裹组件
            <AppContext.Provider value={this.state}>
                <A/>
            </AppContext.Provider>

        </View>
    );
  }

A中有B中有C中有D中有E中有F,代码就不都贴了 只贴F中的代码
F

render() {

        return (
            <View style={styles.view}>
                <Text style={styles.text}>F</Text>
                <TouchableOpacity onPress={()=>{
                }}>
                    <Text style={styles.text}>点击在根视图触发方法</Text>
                </TouchableOpacity>

                <AppContext.Consumer>
                    {
                        context => {
                            // 可以获取到 context 在其他地方进行处理
                            this.contextDataApp = context
                            return <View>
                                <Text style={styles.text}>context App:{context.title}</Text>
                            </View>
                        }
                    }
                </AppContext.Consumer>


            </View>
        );
    }

OK 以上代码就可以解决场景一的问题了,不需要每一层都需要props进行向下传递数据。

开始解决场景二的问题,其实方法和props差不多,传递到Consumer中的value中含有一个方法即可

APP

this.state = {
    title: '123',
    bottomClick:this.bottomClick
}
 bottomClick(){
    console.log('顶层视图 方法调用 bottomClick')
}
<AppContext.Provider value={this.state}>
...
</AppContext.Provider>

F

render() {

        return (
            <View style={styles.view}>
                <Text style={styles.text}>F</Text>
                <TouchableOpacity onPress={()=>{
                    // 在此处也可以调用
                    this.contextDataApp.bottomClick()
                }}>
                    <Text style={styles.text}>点击在根视图触发方法</Text>
                </TouchableOpacity>

                <AppContext.Consumer>
                    {
                        context => {
                            // 可以获取到 context 在其他地方进行处理
                            this.contextDataApp = context
                            return <View>
                                <Text onPress={()=>{
                                    context.bottomClick() //也可以传递参数
                                }} style={styles.text}>context App:{context.title}</Text>
                            </View>
                        }
                    }
                </AppContext.Consumer>


            </View>
        );
    }

ok 以上代码就可以解决场景二的问题了

延伸一点点~

由于 AppContext.Provider 可以对应很多个 AppContext.Consumer

如果 AppContext.Providervalue 确定,有很多个组件需要 value 的值,那岂不是每个组价都要使用 AppContext.Consumer 进行包裹~

蹬蹬蹬蹬蹬蹬蹬蹬丢丢丢 高阶组件登场 ~
高阶组件的介绍和使用点这里喽~

高阶组件

import React, {Component} from 'react'
import {AppContext} from './TestContext'
import App from "../App";
export default (WrappedComponent) => {

    class NewComponent extends Component {
        render(){
            return <AppContext.Consumer>
                {
                    context=><WrappedComponent {...this.props} context={context}/>
                }
            </AppContext.Consumer>
        }
    }

    return NewComponent
}

这里<WrappedComponent {...this.props} context={context}/>传递给普通组件一个 props,普通组件可以使用this.props.context 进行数据操作

使用 E 页面 进行模拟

import React, {PureComponent, Component} from 'react';
import {BackAndroid,
    TouchableOpacity,
    View,
    StyleSheet,
    Dimensions,
    Text
} from "react-native"

const {width, height} = Dimensions.get('window')
import PropTypes from 'prop-types'
import F from './F'
import HocCompenent from './hocComponent'

class E extends Component {

    componentDidMount() {
        // 拿到 顶层视图的 value 
        console.log('E this.props.context=',this.props.context)
    }
    render() {
        return (
            <View style={styles.view}>
                <Text style={styles.text}>E context:{this.props.context.title}</Text>
                <F/>
            </View>
        );
    }
}
const styles = StyleSheet.create({
    view: {
        width: width - 60 - 60 - 60 - 60,
        height: height - 44 - 60 - 60 - 60 - 60,
        backgroundColor: 'red',
        justifyContent: 'center',
        alignItems: 'center'
    },
    text: {
        color: 'white',
        fontSize: 20
    }
})
// 高阶组件使用
export default HocCompenent(E)

最后的最后~

神坑在此~,诸位神魔自行入坑~

Provider不是你想用就可以用的~
最开始使用的旧版本的 API(react16.3.0以前),旧版本限制太多,还是别使用,具体的可自行百度、google。
使用新版本的API,还要看 react 和 reactNative 版本~~😭
新建最新的项目 使用Provider 会报 undefined is not an object (evaluating 'context._currentValue = currentValue') 或者 Cannot set property '_currentValue' of undefined等~错误

要不说google大法好呢,google work ~~ 不过还是要多关注Issues~

image.png

不得不说得吐槽下 开发人员了。。。。这么多人提问题,,,你们新建一个项目用一下不就知道了么,咋不自测下呢~

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,490评论 18 139
  • 一次偶然的机会,读完了三毛的撒哈拉的故事。 刚拿到这本书的时候,我心里就有一种莫名的悲伤,或许是因为知道一些关...
    探凡阅读 223评论 0 0
  • 泡沫 日[うたかた][utakata] 【名词】 (1)泡沫。(水の上に浮く泡)。 泡沫の如く消える/像泡沫一样瞬...
    萌囧囧阅读 423评论 0 0
  • [玫瑰]看到今天小文,让我想起在刘老师智慧父母班课程中听过这样一个故事,叫国王的黄金床:有一位国王,想用黄金打造一...
    悦僮老师阅读 267评论 0 0
  • 2018.9.8 晴 星期六 今天是亲子日记的第一百零一十四天。 今天下班早去小饭桌接着女儿早回家,回到家...
    小徐_390d阅读 148评论 0 0