React的数据传递方式有:
- props:只能父传子
- redux:数据传递不多的时候使用会有点重
- 数据共享:context 上下文
今天来介绍第三种数据共享写法:
背景:
当需要跨层级传递数据时, 如 孙需要父组件的值,那么就需要父给子,子给孙层层传递。数据量又比较小,引入redux略显沉重,props层层传递又笨重。所以这个时候就可以使用context上下文共享数据。
在v16.3之前的context只是官方的实验性API,其实官方是不推荐开发者使用的,但是架不住很多框架依旧在使用它;所以官方在v16.3发布的新的context API,新的API会更加的易用,本文也是以v16.3为准。
注意⚠️:接受的都是最近一层 provider中的value属性
React@16.x 版本之前(旧):
1. 封装 Provide 代码:
- 静态属性 childContextType:声明所需要的数据结构
- PropTypes (prop-types库): 来检查声明类型
- getChildContext():返回需要提供的context值
import PropTypes from 'prop-types'
class Provide extends React.Component{
// 1.声明:检查context共享值的类型
static childContextType = {
// 2.用PropTypes来声明context类型
store: PropTypes.object.isRequired
}
constructor(props){
// 获取传进来的参数 并赋值给store变量
super(props);
this.store = {...props}
}
// 3.返回共享值
getChildContext(){
return {
store: this.store
}
}
render(){
return (
<div>
{this.props.children}
</div>
)
}
}
2. 父组件(提供者)代码 :通过包裹使所有子组件共享数据
import Provide from './provide';
import CustomChild from './CustomChild';
class CustomParent extends React.component{
render(){
return (
// 使用provide共享信息,所有北provide组件包括的均可以获取到共享的data信息
<Provide data={{name:"xx",sex:"男"}}>
// 子组件中会有name,sex的信息
<CustomChild/>
<CustomChild1/>
</Provide>
)
}
}
3. 子组件(消费者)代码:通过this.context.xx 获取
class CustomChild extends React.component{
render(){
return (
<div>
共享信息:{this.context.store.name}
</div>
)
}
}
备注:
propTypes校验: 防止缺陷并规划组件使用的数据种类
prop-types库: 只在开发模式中进行类型评估,运行在生产环境不会耗费额外的精力 react@16之前是核心react库的一部分, 现在作为prop-types包单独存在
React@16 以后的版本(新):
React提供了一个createContext的方法,该方法返回一个包含了:
- 参数:defaultValue默认值:在没有
<TopContext.Provider/>包裹的时候才会生效 - 返回值:
- Provider对象:提供者 传参使用value
- Consumer对象:消费者 必须以函数方式获取
最顶层组件(提供者):Provider
import {createContext} from "react";
const defaultValue={
author:"李三"
}
export const TopContext = createContext(defaultValue);
//...省略其他代码
this.state={
author:"赵四"
}
render() {
return (
<div className="App">
<TopContext.Provider value={this.state}>
<Parent/>
</TopContext.Provider>
</div>
);
}
// ...省略其他代码
子组件(消费者):取值必须是函数
import { Component } from "react";
import { TopContext } from "../../const/context";
class ShowAuthor extends Component {
render() {
return (
<>
<TopContext.Consumer>
{value => <p>作者: {value.author}</p>}
</TopContext.Consumer>
</>
)
}
}
export default ShowAuthor;
从上面代码可以看出,每个子组件使用的时候都要引入<TopContext.Consumer>并包裹,看起来有些重复。所以我们有2种优化方式:
方式一:可以封装一个connect方法:
- 参数:mapstate函数-> 返回共享的值
- 调用函数的参数:组件本身
- 返回值:一个函数。所以调用方式是
connect(参数)(组件);
connect 方法:
import { TopContext } from "../constant/context"
const connectContext = (mapState) => {
// 该函数实际是一个高阶函数工厂
return (WrappedComponent) => {
const Component = (props) => {
return (
<TopContext.Consumer>
{
value => {
let mappedProps = mapState(value);
return <WrappedComponent {...props} {...mappedProps} />
}
}
</TopContext.Consumer>
)
}
Component.displayName = `connect(${WrappedComponent.displayName || WrappedComponent.name || "Component"})`
return Component;
}
}
export default connectContext;
子组件(消费组件):
import connectContext from "../../js/connect";
const Detail = ({author})=> {
return (
<div>
<p>共享的数据作者:{author}</p>
</div>
)
}
const mapState = (state)=>{
return {...state}
}
export default connectContext(mapState)(Detail);
方式二:使用contextType + this.context取值
子组件(消费组件):
export default class ConnectTypeEg extends PureComponent {
render() {
return (
<div>
通过赋值给[contextType]方式 取值:{this.context.author}
</div>
)
}
}
ConnectTypeEg.contextType = TopContext;
HOOK: useContext
Hook 是 React 16.8.0 版本增加的新特性,可以在
函数组件中使用 state以及其他的 React 特性。只能在函数组件中使用
父组件:创建+包裹
export const ContactContext = createContext({});
function parent(){
<ContactContext.Provider value={{ itemData: res }} >
<ShowName />
<ShowSex/>
</ContactContext.Provider>
}
子组件接收数据:useContext(父定义的context)
const ShowName = () => {
const {itemData:{name}} = useContext(ContactContext);
return (
<span className='name'>{name}</span>
)
}
总结:
context 可以共享数据
- 16.3之前的老版本
- 提供者:
getChildContext + static childContextType - 消费者:
this.context.xx 取值
- 提供者:
- 16.3之后的版本:
- 类组件:
React.createContext- 提供者:TopContext.Provider
- 消费者:TopContext.Consumer
- 优化方式一:
组件.contextType= TopContext; this.context.xx取值 - 优化方式二:
- 封装connect函数,直接从组件参数获取
- 优化方式一:
- 函数组件(hook):
-
提供者:createContext.Provider
-
消费者:useContext(TopContext)返回值
-
- 类组件: