React Context状态管理的实现是怎样的
Admin 2022-08-09 群英技术资讯 892 次浏览
这篇文章主要介绍了React Context状态管理的实现是怎样的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇React Context状态管理的实现是怎样的文章都会有所收获,下面我们一起来看看吧。redux、mobx、recoil
但是对于小项目,我们完全可以自己封装一个状态管理,减少一个包的安装就可以减小打包以后的项目体积。 主要分两步:
stores/index.js 文件中首先需要调用 createContext
export const MyContext = React.createContext({list: [], data: null, time: Date.now()});
createContext 中的参数是默认值,只有当组件所处的树中没有匹配到 Provider 时,其参数才会生效。
每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中匹配离自身最近的Provider,并从中读取到当前的 context 值。
context 可以设置一个displayName 的属性, 可以方便在React DevTool 对该context调试。
MyContext.displayName = 'MyManagementDisplayName';
Provider 接收一个 value 属性,传递给消费组件。 Context 能让你将这些数据向组件树下所有的组件进行“广播”,所有的组件都能访问到这些数据,也能访问到后续的数据更新。
这里我们封装一个父组件用来包裹其他子组件。
import { createContext, useReducer } from 'react';
// 纯函数reducer
function reducer(state, action) {
// action包括 具体的类型type,
// 除了 `type` 之外,action 对象的结构其实完全取决于你自己。
// 这里使用了payload代表dipatch传过来的数据
switch(action.type) {
case 'list':
return ({...state, list: action.payload});
case 'data':
return ({...state, data: action.payload});
case 'time':
return ({...state, time: action.payload});
default:
return state;
}
}
const list = [{num: 0, key: 0}, {num: 1, key: 1}, {num: 2, key: 2}];
export const MyContext = createContext({list: [], data: null, time: Date.now()});
function ContextProvider({children}) {
const [state, dispatch] = useReducer(
reducer,
{list: list, data: null, time: Date.now()}
);
const value = {
state,
dispatch
}
return <MyContext.Provider value={value}>
{children}
</MyContext.Provider>
}
export default ContextProvider;
这里用到了useReducer, 用过redux的同学一定非常熟悉,这是因为redux的作者 dan abramov 加入了react开发团队, 是react的主要开发者。 第一个参数是一个处理数据的纯函数,第二个参数是 initialValue。 useReducer还有另一种用法可以接受函数作为第三个参数,可以惰性地创建初始 state,这不是本文的重点,感兴趣的同学可以自行查询文档学习。
在入口文件index.js中 用 ContextProvider 包裹 App 组件
import ReactDOM from 'react-dom';
import App from './App';
import './styles/index.less';
import ContextProvider from './stores';
ReactDOM.render(
<ContextProvider><App /></ContextProvider>,
document.getElementById('root')
);
有3种方式
import {MyContext} from '@/store';
class MyClass extends React.Component {
static contextType = MyContext;
// 引入的MyContext 赋值给静态属性 contextType后,
// React可以让你使用 `this.context` 来获取最近 Context 上的值。
componentDidMount() {
let value = this.context;
/* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* 基于 MyContext 组件的值进行渲染 */
}
}
<context.Consumer>
{value => /* 基于 context 值进行渲染* /}
</context.Consumer>
这是使用 hook 方式, 也是目前最流行的用法,后面的例子主要使用这个方式来演示。 因为我们要在很多需要全局状态的子组件使用,所以我们可以封装一下。
在 hooks/useStores.js
import {MyContext} from '@/stores';
import React from 'react';
// 封装代码以复用
const useStores = () => React.useContext(MyContext);
export default useStores;
下面我们通过两个组件,分别演示 获取数据并展示 和 更新全局数据
views/footer/index.js
在此组件里获取全局数据并展示
import { useEffect } from 'react';
import useStores from '../../hooks/useStores';
function Footer() {
const { state } = useStores();
const { time, list } = state;
useEffect(() => {
console.log('Footer page rendered!!!')
})
return (
<div style={{ height: 200 }}>
<div>time now is {time}</div>
<div>
list is
{list.map((item) => (
<span
style={{
background: 'pink',
padding: '0 10px',
border: '1px solid',
marginRight: '10px'
}}
key={item.key}
>
{item.num}
</span>
))}
</div>
</div>
);
}
export default Footer;
views/header/index.js
我们在此组件里更新全局数据
import useStores from '../../hooks/useStores';
import { Link } from 'react-router-dom';
import { useEffect } from 'react';
function Header() {
// 解构获取 dispatch 方法
const { dispatch } = useStores();
const handleList = () => {
const payload = [...new Array(3)].map(() => {
const key = Math.random();
const num = Math.floor(key * 100);
return ({
key, num
});
})
// 更新数据,订阅状态的组件都会获取更新通知并取到最新数据
dispatch({ type: "list", payload });
};
return (
<div style={{ height: 100 }}>
<button onClick={() => dispatch({ type: 'time', payload: Date.now() })}>
time
</button>
<button onClick={handleList}>list</button>
</div>
);
}
export default Header;

点击 header 中的按钮,footer 里的 time list 都会响应改变,获取到最新的值并渲染展示。
我们通过封装顶层组件提供全局数据,子组件获取和更新数据, 完全基于 React 实现了一个简单的状态管理。
当然 Context 是可以嵌套多层的,同学们可以自行尝试
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
今天给大家分享的是关于vue图片裁剪组件的内容,下文对vue图片裁剪组件的使用有详细的介绍及示例代码,有需要的朋友可以参考。本文介绍的组件是基于vue-cropper二次封装,接下来我们一起了解看看。
跨域指浏览器不允许当前页面的所在的源去请求另一个源的数据,下面这篇文章主要给大家介绍了关于VUE跨域详解以及常用解决跨域的方法,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
方法:1、利用removeClass()方法删除元素所有的类名,语法为“元素对象.removeClass();”;2、利用addClass()方法将想要替换的类名添加即可,语法为“元素对象.addClasss("新的类名");”。
JS中的String.raw方法,下文有实例供大家参考,对大家了解操作过程或相关知识有一定的帮助,而且实用性强,希望这篇文章能帮助大家,下面我们一起来了解看看吧。
区别:1.JS运行在浏览器端,用于用户的交互效果,NodeJS运行在服务器端,用于服务器的操作,例如,Web服务器创建,数据库的操作,文件的操作等2.JS运行在浏览器端,存在多个JS解释器,存在兼容性的问题,而NodeJS只有V8引擎一种解释器,不存在兼容性问题3.两者都有内置对象,自定义对象,有不同的宿主对象 词语解释(js为例):js的内置对象、自定义对象,和宿主对象javasc
成为群英会员,开启智能安全云计算之旅
立即注册关注或联系群英网络
7x24小时售前:400-678-4567
7x24小时售后:0668-2555666
24小时QQ客服
群英微信公众号
CNNIC域名投诉举报处理平台
服务电话:010-58813000
服务邮箱:service@cnnic.cn
投诉与建议:0668-2555555
Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008