美文网首页RN知识
React-Native Context跨层级的组件通信

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

作者: 精神病患者link常 | 来源:发表于2018-11-09 17:27 被阅读232次
    场景

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

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

    相关文章

      网友评论

        本文标题:React-Native Context跨层级的组件通信

        本文链接:https://www.haomeiwen.com/subject/qdeoxqtx.html