React 上下文Context的使用

依照3W学习原则的模式出发来思考Context的使用问题,在学会如何使用Context之前,首先我们需要来思考一下几个问题:

  •  Context是什么?

  •  为什么需要用Context?

  •  怎么样使用Context?

使用之前较为清晰的了解使用研究对象更加有助于我们的认知和加深我们对Context的理解程度。

Context是什么?

Context(上下文)一直在各种语言、平台之中占据较为重要的地位,它是整个程序的链接者,一直贯穿应用的整个生命周期。React中的Context也类似,Context旨在为React复杂嵌套的各个组件提供一个生命周期内的统一属性访问对象,从而避免我们出现当出现复杂嵌套结构的组件需要一层层通过属性传递值得问题。

其设计之初的构想就是为了提供一个组件树形结构内的一个数据共享的容器。

为什么需要用Context?

为何要用Context,前文也有所介绍,其主要原因还是由于不使用Context无法完成某些业务场景或者是实现起来很难。我们可以通过两张图来了解下在是否使用Context场景下,其对应的属性值传递的过程。来帮助我们了解为什么Context非用不可。

  • 不使用Context
    React 上下文Context的使用

传统方式不使用Context,数据的传递如上图属于嵌套关系,只能通过有父子包含关系的组件一层层进行传递,数据传递的层级随着结构的增加而成倍增加。

  • 使用Context

React 上下文Context的使用

使用Context方式进行数据的共享,各个树状组件均可通过统一的Consumer统一访问全局的Consumer共享数据。

怎么样使用Context?

Context的使用主要分为创建、插入、访问三个流程,通过创建context对象提供共享组件,将Provider指定嵌套至需要使用共享数据的顶层结构,然后各后代组件通过context访问共享数据(变量、常量、方法等)。接下来,通过一个封装的登录组件来学习使用Context。

创建一个context

首先,我们需要创建一个待使用的context对象放在一边,等待使用的使用导入,我们可以将其封装成一个单独的js文件并将其export:

// 创建文件LoginContext.jsimport { createContext } from 'react';const LoginContext = createContext({});export default LoginContext;

Provider包装一个组件

为了降低程序各个功能块的耦合性,防止Context共享数据的滥用和误用。一般的,我们可以将单独的一个模块(需要使用共享数据),进行一层封装,封装成一个单独的组件使用,如全局的多语言切换就可以在顶级组件App内封装。

//创建一个LoginFrom.jsimport { Tabs, Form } from 'antd';import React, { useState } from 'react';import useMergeValue from 'use-merge-value';import classNames from 'classnames';import LoginContext from './LoginContext';import LoginItem from './LoginItem';import LoginSubmit from './LoginSubmit';import LoginTab from './LoginTab';import styles from './index.less';const Login = props => {
const { className } = props;
const [tabs, setTabs] = useState([]);
const [active, setActive] = useState();
const [type, setType] = useMergeValue('', {
value
: props.activeKey,
onChange
: props.onTabChange,
});
const TabChildren = [];
const otherChildren = [];
React.Children.forEach(props.children, child => {
if (!child) {
return;
}

if (child.type.typeName === 'LoginTab') {
TabChildren.push(child);
} else {
otherChildren
.push(child);
}
});
return (
<LoginContext.Provider
value
={{
tabUtil
: {
addTab
: id => {
setTabs
([...tabs, id]);
},
removeTab
: id => {
setTabs
(tabs.filter(currentId => currentId !== id));
},
},
updateActive
: activeItem => {
if (active[type]) {
active
[type].push(activeItem);
} else {
active
[type] = [activeItem];
}

setActive
(active);
},
}}
>
<div className={classNames(className, styles.login)}>
<Form
form
={props.from}
onFinish
={values => {
if (props.onSubmit) {
props
.onSubmit(values);
}
}}
>
{tabs.length ? (
<React.Fragment>
<Tabs
animated
={false}
className
={styles.tabs}
activeKey
={type}
onChange
={activeKey => {
setType
(activeKey);
}}
>
{TabChildren}
</Tabs>
{otherChildren}
</
React.Fragment>
) : (
props
.children
)}
</Form>
</
div>
</LoginContext.Provider>
);};Login.Tab = LoginTab;Login.Submit = LoginSubmit;Login.UserName = LoginItem.UserName;Login.Password = LoginItem.Password;Login.Mobile = LoginItem.Mobile;Login.Captcha = LoginItem.Captcha;export default Login;

从上面的代码我们可以看到LoginContext.Provider 提供了一个共享对象:

 <LoginContext.Provider
value={{name:'title',value:'Provider'}}
>
......

</LoginContext.Provider>

一般的我们通过设置value值来指定共享数据的内容。

调用共享数据

通过上面的创建和定义,接下来我们就可以在Provider包含组件内进行共享数据的调用获取,而不需要一层层的去传递了。

//创建文件LoginTab.jsimport React, { useEffect } from 'react';import { Tabs } from 'antd';import LoginContext from './LoginContext'; // 创建的共享contextconst { TabPane } = Tabs;const generateId = (() => {
let i
= 0;
return (prefix = '') => {
i
+= 1;
return `${prefix}${i}`;
};})();const LoginTab = props => {
useEffect
(() => {
const uniqueId = generateId('login-tab-');
const { tabUtil } = props;

if (tabUtil) {
tabUtil
.addTab(uniqueId);
}
}, []);
const { children } = props;
return <TabPane {...props}>{props.active && children}</TabPane>;};const WrapContext = props => (
<LoginContext.Consumer>
{value => <LoginTab tabUtil={value.tabUtil} {...props} />}
</LoginContext.Consumer>
); /
/ 标志位 用来判断是不是自定义组件WrapContext.typeName = 'LoginTab';export default WrapContext;

上面的代码可以看出,我们可以通过LoginContext.Consumer包裹器包裹获取共享数据Context,其获取数据格式:

<LoginContext.Consumer>    {value => (/**这里可以渲染对应的jsx代码,value就是Provider中提供的value**/)}  </LoginContext.Consumer>

原文始发于微信公众号(胖蔡话前端):React 上下文Context的使用

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/223222.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!