场景
如图: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中取的就是Provider的value
-
Provider和Consumer是 一一对应滴~BConsumer是取不到AProvider中的value滴~-
Provider组件的value值发生变更时,其内部组件树中对应的Consumer组件会接收到新值并重新执行 内部 函数。此过程不受shouldComponentUpdete方法的影响。-
Provider组件利用Object.is检测value的值是否有更新。注意Object.is和===的行为不完全相同呀
Provider
<Provider value={/* some value */}>
...
</Provider>
接收一个 value 属性传递给Provider 的后代 Consumers。一个 Provider 可以链接到多个 Consumers。Providers 可以被嵌套以覆盖组件树内更深层次的值。
Provider 包裹的组价内部 可以通过Consumers访问到 Provider 的value值
⚠️⚠️⚠️注意:尽量不要在这里
<Provider value={/* some value */}>给value赋具体的值,比如<Provider value={{key: 'value',key1: 'value1'...}}
因为只要render执行一次,Provider的value就会返回一个全新的{},不管里面的值是否改变,与Provider对应的Consumer都会重新执行一次。为了减少不必要的刷新,尽量使用state,然后配合PureComponent进行性能优化
Consumer
<Consumer>
{
value => {
coding......
}
}
</Consumer>
注意 Consumer 内部是一个方法,有一个value。这个参数就是 Provider 的 value。得到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.Provider 的 value 确定,有很多个组件需要 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
不得不说得吐槽下 开发人员了。。。。这么多人提问题,,,你们新建一个项目用一下不就知道了么,咋不自测下呢~










网友评论