React Context状态管理的实现是怎样的
Admin 2022-08-09 群英技术资讯 299 次浏览
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进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
本文实例讲述了Node.js API详解之 util模块用法。分享给大家供大家参考,具体如下
这些年来,ES6 将js的可用性提升到一个新的水平时: 箭头函数、类等等,这些都很棒。箭头函数是最有价值的新功能之一,有很多好文章描述了它的上下文透明性和简短的语法。
Angular指令如何保持关注点的分离?本篇文章带大家了解一下通过 Angular 指令保持关注点分离的方法,希望对大家有所帮助!
这篇文章主要介绍了JavaScript中时间格式化新思路toLocaleString(),研究Object对象的时候,看到了 toLocaleString() 这个方法可以很简单的实现时间格式化,下面来看看toLocaleString() 的详细内容吧,需要的朋友可以参考一下
这篇文章主要为大家详细介绍了vue+elementUI实现分页效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008