RN知识

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

2018-11-09  本文已影响232人  精神病患者link常
场景

如图: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

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

上一篇 下一篇

猜你喜欢

热点阅读