網站首頁 編程語言 正文
前言
在大多數情況下,我們開發項目都需要一個狀態管理,方便我們在全局共享狀態庫,在React生態里比較流行的幾個庫
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種方式
- Class Component 內獲取(本方法僅能訂閱 1 個 context)
- context.Consumer
- useContext
class Component 方式
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
<context.Consumer> {value => /* 基于 context 值進行渲染* /} </context.Consumer>
useContext
這是使用 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 是可以嵌套多層的,同學們可以自行嘗試
參考
- React Context
- React useReducer
原文鏈接:https://juejin.cn/post/7118946120499200031
相關推薦
- 2022-05-23 Oracle配置dblink訪問PostgreSQL的操作方法_oracle
- 2022-07-15 Qt項目打包的實現步驟_C 語言
- 2022-04-10 Android外部存儲無法訪問問題解決方法_Android
- 2022-04-20 Prism區域管理器IRegionManager用法介紹_實用技巧
- 2022-03-20 .NET+Sqlite支持加密的操作方法_實用技巧
- 2022-11-13 Error: EACCES: permission denied, access '/usr/loc
- 2023-08-13 什么是單點登錄?
- 2022-04-23 css3新特性transition初體驗
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支