日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
ReactHooks和Redux哪個(gè)才是更好的狀態(tài)管理策略?

如果您是一名React開(kāi)發(fā)人員,那么一定對(duì)狀態(tài)管理策略并不陌生。當(dāng)我們?cè)谑褂肦eact去構(gòu)建Web應(yīng)用時(shí),所有信息都被保存在所謂的狀態(tài)之中。我們只需要更新該狀態(tài),即可實(shí)現(xiàn)對(duì)Web應(yīng)用的更新。而狀態(tài)管理,是指在應(yīng)用程序的生命周期中,處理各種事件,并控制不同組件之間數(shù)據(jù)傳遞的過(guò)程。

成都創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括玉山網(wǎng)站建設(shè)、玉山網(wǎng)站制作、玉山網(wǎng)頁(yè)制作以及玉山網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃等。多年來(lái),我們專(zhuān)注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,玉山網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶(hù)以成都為中心已經(jīng)輻射到玉山省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶(hù)的支持與信任!

一直以來(lái),我們都習(xí)慣于使用針對(duì)JavaScript應(yīng)用的、流行且強(qiáng)大的Redux庫(kù),作為狀態(tài)容器。而React本身在其16.8版中已增加了Hooks。在本文中,我將根據(jù)自己在使用React SDK,構(gòu)建生產(chǎn)級(jí)數(shù)據(jù)可視化工具過(guò)程中的經(jīng)驗(yàn),和您探討這兩種狀態(tài)管理的方法,并介紹作為第三種方法的混合使用。

狀態(tài)管理戰(zhàn)略規(guī)劃

首先,讓我們來(lái)考慮狀態(tài)管理的兩個(gè)難題:需要存儲(chǔ)什么狀態(tài),以及為什么要如此。畢竟,在數(shù)據(jù)可視化的應(yīng)用中,并非所有狀態(tài)都是相同的。

如下應(yīng)用示例所示,我們希望通過(guò)圖表中顯示的節(jié)點(diǎn)和鏈接,以獲悉當(dāng)前的各個(gè)連接,以及與時(shí)間線組件共享的數(shù)據(jù),進(jìn)而甄別出數(shù)據(jù)集中的時(shí)間戳。其Sidebar包括了用于搜索和更新圖表、及時(shí)間線的UI元素。簡(jiǎn)單而言,我們的目標(biāo)就是實(shí)現(xiàn)如下圖形和時(shí)間線的可視化。具體請(qǐng)參見(jiàn)--KronoGraph(。

在狀態(tài)管理策略的規(guī)劃階段,我們可以通過(guò)在軸上繪制狀態(tài),以了解正在處理的具體內(nèi)容:

如上圖所示,我們?cè)诖怂裱脑瓌t為:

  • 條目類(lèi)型:除非您正在構(gòu)建一個(gè)通用應(yīng)用,否則圖表和時(shí)間線中的節(jié)點(diǎn)類(lèi)型(如:人員、地點(diǎn)、車(chē)輛)都應(yīng)當(dāng)盡可能是靜態(tài)的。由于我們可以提前定義它們,因此它們不需要帶有狀態(tài),可以位于存儲(chǔ)庫(kù)的配置文件中。
  • 條目樣式:包含了每個(gè)節(jié)點(diǎn)和鏈接類(lèi)型的核心樣式,以及它們的預(yù)期邏輯。
  • 主題選擇:為用戶(hù)提供了在暗模式與亮模式之間切換的選項(xiàng),并通過(guò)該狀態(tài)的變化,去跟蹤用戶(hù)的偏好。
  • UI狀態(tài):UI狀態(tài)包括靜態(tài)和臨時(shí)等。雖然我們沒(méi)有必要在狀態(tài)中,存儲(chǔ)所有關(guān)于表單的交互,但是需謹(jǐn)防那些可能導(dǎo)致應(yīng)用處于無(wú)響應(yīng)狀態(tài)的常見(jiàn)錯(cuò)誤。
  • 條目位置和時(shí)間線范圍:網(wǎng)絡(luò)中的節(jié)點(diǎn)位置可能并不固定:
  1. 在圖表中,用戶(hù)可以根據(jù)偏好進(jìn)行布局,并手動(dòng)定位節(jié)點(diǎn)。
  2. 在時(shí)間線中,用戶(hù)可以放大其感興趣的時(shí)間范圍。
  3. 在不同的會(huì)話(huà)中,通過(guò)位置的保持,用戶(hù)可以從上一次中斷處繼續(xù)。
  • 撤消與重做棧:在高級(jí)應(yīng)用中,我們需要通過(guò)設(shè)計(jì),讓用戶(hù)能夠在各自的當(dāng)前會(huì)話(huà)中,保留撤消與重做數(shù)據(jù)的權(quán)限。
  • 來(lái)自API的數(shù)據(jù):功能強(qiáng)大的應(yīng)用程序,需要將那些從外部端點(diǎn)或API接收來(lái)的、動(dòng)態(tài)且臨時(shí)的數(shù)據(jù)緩存起來(lái),并保存它們?cè)趹?yīng)用中的狀態(tài)。

狀態(tài)管理的方法

有了前面狀態(tài)管理的規(guī)劃,我們來(lái)考慮應(yīng)用中的數(shù)據(jù)層次結(jié)構(gòu)。目前,我們有三種主要的狀態(tài)管理方法可供選擇:

  • 處理組件中的狀態(tài),并按需使用Hook在狀態(tài)間進(jìn)行傳遞。這種方法通常被稱(chēng)為“prop drilling”或“提升狀態(tài)”,常被推薦用于基礎(chǔ)類(lèi)的應(yīng)用。
  • 使用某種全局存儲(chǔ),以便所有組件都可訪問(wèn)。Redux之類(lèi)的庫(kù)可以提供該功能。
  • 使用混合方法,將Hook與那些經(jīng)過(guò)慎重選擇的重要狀態(tài)相結(jié)合。

下面,讓我們通過(guò)上述數(shù)據(jù)可視化的應(yīng)用,來(lái)進(jìn)一步探索這三種方法。

Redux狀態(tài)管理

自2015年被發(fā)布以來(lái),Redux已經(jīng)成為了React生態(tài)系統(tǒng)的關(guān)鍵部分。它使用不變性(immutability)來(lái)簡(jiǎn)化應(yīng)用程序的開(kāi)發(fā)和邏輯設(shè)計(jì)。通過(guò)將處于某種狀態(tài)的所有條目,強(qiáng)制設(shè)置為不變性,我們可以跟蹤對(duì)于數(shù)據(jù)的任何更改,進(jìn)而避免可能導(dǎo)致意外錯(cuò)誤發(fā)生的數(shù)據(jù)突變。

雖然Redux目前仍是狀態(tài)復(fù)雜的大型應(yīng)用的絕佳選擇,但是隨著時(shí)間的推移,它變得日漸臃腫。為了協(xié)助降低其復(fù)雜性,Redux Toolkit于2019年應(yīng)運(yùn)而生,并成為了Redux的首推方式。

一致性的狀態(tài)更新

Redux的一個(gè)核心概念是reducer。對(duì)于那些具有函數(shù)編程經(jīng)驗(yàn)的人而言,這是一個(gè)能夠接受多個(gè)輸入,并將其減少為單個(gè)輸出的函數(shù)。在狀態(tài)管理中,該擴(kuò)展能夠讓您通過(guò)采用一個(gè)或多個(gè)狀態(tài)的更新指令,為圖表生成一致性的狀態(tài)更新。

讓我們來(lái)考慮一個(gè)標(biāo)準(zhǔn)化的圖形可視化用例:在圖表中添加和刪除節(jié)點(diǎn)。為了在全局存儲(chǔ)中創(chuàng)建一個(gè)狀態(tài)“切片”,我們?cè)趕tore.js中創(chuàng)建了如下代碼:

JavaScript

 
 
 
 
  1. import { configureStore } from '@reduxjs/toolkit'; 
  2. import itemsReducer from '../features/chart/itemsSlice'; 
  3.   
  4. export const store = configureStore({ 
  5.  reducer: { 
  6.    items: itemsReducer 
  7.  } 
  8. }); 

為了讓?xiě)?yīng)用程序中的其他組件能夠訪問(wèn)該存儲(chǔ),我們可以對(duì)應(yīng)用程序進(jìn)行如下“包裝”:

JavaScript

 
 
 
 
  1. importReactfrom 'react'; 
  2. import ReactDOM from 'react-dom'; 
  3. import './index.css'; 
  4. import App from './App'; 
  5. import { store } from './app/store'; 
  6. import { Provider } from 'react-redux'; 
  7. import * as serviceWorker from './serviceWorker'; 
  8.   
  9. ReactDOM.render( 
  10.   
  11.     
  12.       
  13.     
  14.  , 
  15.  document.getElementById('root') 
  16. ); 

其中的Provider段意味著,其任何下游都可以訪問(wèn)該存儲(chǔ)。在itemsSlice.js中,我們?yōu)楦鱾€(gè)條目定義了狀態(tài)切片:

JavaScript

 
 
 
 
  1. import { createSlice, createEntityAdapter } from '@reduxjs/toolkit'; 
  2.   
  3. export const itemsAdapter = createEntityAdapter(); 
  4. const initialState = itemsAdapter.getInitialState(); 
  5.   
  6. export const itemsSlice = createSlice({ 
  7.  name: 'items', 
  8.  initialState, 
  9.  reducers: { 
  10.    addItems: itemsAdapter.addMany, 
  11.    addItem: itemsAdapter.addOne, 
  12.    removeItems: itemsAdapter.removeMany, 
  13.    removeItem: itemsAdapter.removeOne, 
  14.  }, 
  15. }); 
  16.   
  17. export const { addItems, addItem, removeItems, removeItem } = itemsSlice.actions; 
  18.   
  19. export const { select, selectAll, selectTotal } = itemsAdapter.getSelectors((state) => state.items); 
  20.   
  21. export default itemsSlice.reducer; 

通過(guò)上述代碼段,我們可以獲悉:

  • ReGraph的條目是各種通過(guò)ID索引的節(jié)點(diǎn)和鏈接對(duì)象。其核心數(shù)據(jù)結(jié)構(gòu)十分常見(jiàn)。Redux Toolkit會(huì)通過(guò)一些輔助函數(shù),來(lái)處理此類(lèi)格式數(shù)據(jù)。在此,我們用到了由createEntityAdapter提供的addMany、addOne、removeMany、以及removeOne等功能。
  • 在Redux中,Selector允許我們從存儲(chǔ)中獲取一個(gè)狀態(tài)片。我可以利用getSelectors適配器,來(lái)避免自行編寫(xiě)狀態(tài)查詢(xún)代碼。
  • 最后,我們導(dǎo)出所有內(nèi)容,以便在應(yīng)用程序的其他地方使用。

在應(yīng)用的其他代碼中,我們還用到了store、reducer和selectors:

JavaScript

 
 
 
 
  1. import React from 'react'; 
  2. import { useSelector, useDispatch } from 'react-redux'; 
  3.   
  4. import { Chart } from 'regraph'; 
  5. import { addItems, addItem, removeItems, removeItem, selectAll, selectTotal } from './itemsSlice'; 
  6.   
  7. import mapValues from 'lodash/mapValues'; 
  8.   
  9. import styles from './NetworkChart.module.css'; 
  10.   
  11. const colors = ['#173753', '#6daedb', '#2892d7', '#1b4353', '#1d70a2']; 
  12.   
  13. const defaultNodeStyle = (label) => ({ 
  14.  label: { 
  15.    text: `User ${label}`, 
  16.    backgroundColor: 'transparent', 
  17.    color: 'white', 
  18.  }, 
  19.  border: { width: 2, color: 'white' }, 
  20.  color: colors[(label - 1) % colors.length], 
  21. }); 
  22.   
  23. const styleItems = (items, theme) => { 
  24.  return mapValues(items, (item) => { 
  25.    if (item.id1) { 
  26.      return { ...defaultLinkStyle(item.id), ...theme[item.type] }; 
  27.    } else { 
  28.      return { ...defaultNodeStyle(item.id), ...theme[item.type] }; 
  29.    } 
  30.  }); 
  31. }; 
  32.   
  33. export function NetworkChart() { 
  34.  const dispatch = useDispatch(); 
  35.   
  36.  const items = useSelector(selectAll); 
  37.  const itemCount = useSelector(selectTotal); 
  38.   
  39.  const theme = { user: {} }; 
  40.  const styledItems = styleItems(items, theme); 
  41.   
  42.  return ( 
  43.     
  44.      
  45.        items={styledItems} 
  46.        animation={{ animate: false }} 
  47.        options={{ backgroundColor: 'rgba(0,0,0,0)', navigation: false, overview: false }} 
  48.      /> 
  49.       
  50.        
  51.          className={styles.button} 
  52.          aria-label="Add items" 
  53.          onClick={() => dispatch(addItem({ id: itemCount + 1, type: 'user' }))} 
  54.        > 
  55.          Add User 
  56.         
  57.        
  58.          className={styles.button} 
  59.          aria-label="Remove Items" 
  60.          onClick={() => dispatch(removeItem(itemCount))} 
  61.        > 
  62.          Remove User 
  63.         
  64.      
 
  •    
  •  
  •  ); 
  • 通過(guò)使用Redux Hook的suseSelector,我們可以輕松利用切片代碼,來(lái)提供選擇器。同時(shí),其useDispatch允許我們根據(jù)狀態(tài)的“調(diào)度(dispatch)”動(dòng)作(Redux的另一個(gè)實(shí)用部分),去變更狀態(tài)。

    使用Redux管理狀態(tài)去添加和刪除節(jié)點(diǎn)

    Redux Toolkit使用時(shí)下流行的不變性庫(kù)--Immer,對(duì)狀態(tài)進(jìn)行“純凈”地更新,而無(wú)需額外編寫(xiě)復(fù)雜的克隆和更新邏輯。在此,我們直接在組件中設(shè)置了圖表項(xiàng)的樣式。

    當(dāng)您從外部來(lái)源獲取數(shù)據(jù)時(shí),應(yīng)用程序的狀態(tài)和數(shù)據(jù)庫(kù)的存儲(chǔ)之間,很難被清晰地界定。與Redux Toolkit同源的RTK Query則通過(guò)與諸如react-query之類(lèi)的庫(kù)相配合,避免了從零開(kāi)始編寫(xiě)緩存等功能。

    如果您的應(yīng)用單純依賴(lài)Redux,那么可以將整個(gè)應(yīng)用的狀態(tài)放在全局存儲(chǔ)中,以便每個(gè)組件都能訪問(wèn)它。當(dāng)然,實(shí)際上只有某一些可視化組件的狀態(tài),需要通過(guò)Hooks和Redux的混合方法,實(shí)現(xiàn)存儲(chǔ)。

    Prop Drilling

    著名的軟件工程教育者--Kent C. Dodds曾提出了一個(gè)重要的觀點(diǎn):應(yīng)保持狀態(tài)盡可能地靠近需要的地方。對(duì)于上述示例,這意味著如果我們希望在圖表和時(shí)間線組件之間共享數(shù)據(jù),則可以通過(guò)Prop Drilling來(lái)簡(jiǎn)化并實(shí)現(xiàn)。這將是一種跨組件共享狀態(tài)的有效且純凈的方式。也就是說(shuō),如果我們將狀態(tài)帶到VisualizationContainer應(yīng)用中,則可以將數(shù)據(jù)作為prop傳遞到每個(gè)組件處。當(dāng)然,如果我需要在復(fù)雜的層次結(jié)構(gòu)中上下傳遞,則仍可以使用Redux。

    憑借著其強(qiáng)大的API和一些精心設(shè)計(jì)的prop,ReGraph在控制其內(nèi)部狀態(tài)方面,非常有效。我們甚至不需要讓過(guò)多的prop流轉(zhuǎn)到圖表的組件之外。

    React Hooks

    就示例中的圖表組件而言,我們可以使用simpleuseState和useRefHooks,來(lái)處理狀態(tài)中的基本配置。ReGraph可以將那些對(duì)于狀態(tài)的多次更新處理,通過(guò)單獨(dú)調(diào)用useState對(duì)方式來(lái)實(shí)現(xiàn),進(jìn)而免去了prop組的頻繁更新。

    JavaScript

     
     
     
     
    1. const [layout, setLayout] = useState(defaults.layout); 
    2. setLayout({name: 'sequential'}) 

    對(duì)于使用過(guò)Redux的人來(lái)說(shuō),Hook的useReducer以及如下代碼段,一定不會(huì)陌生。

    JavaScript

     
     
     
     
    1. import React, { useState, useReducer, useCallback } from 'react'; 
    2.   
    3. const [combine, combineDispatch] = useReducer(combineReducer, defaults.combine) 
    4.   const combineItems = useCallback(property => combineDispatch({ type: 'COMBINE', property }), []) 
    5.   const uncombineItems = useCallback(property => combineDispatch({ type: 'UNCOMBINE', property }), []) 
    6.   
    7.   
    8. function combineReducer(combine, action) { 
    9.   const newCombine = { ...combine }; 
    10.   if (action.type === 'COMBINE') { 
    11.     newCombine.properties.push(action.property); 
    12.     newCombine.level = combine.level + 1; 
    13.   } 
    14.   else if (action.type === 'UNCOMBINE') { 
    15.     newCombine.properties.pop(); 
    16.     newCombine.level = combine.level - 1; 
    17.   } else { 
    18.     throw new Error(`No action ${action.type} found`); 
    19.   } 
    20.   return newCombine; 

    值得注意的是,沒(méi)有了Redux Toolkit的幫助,我們需要人工更新已組合的對(duì)象。這就意味著,更多的代碼需要被編寫(xiě)。在上述ReGraph之類(lèi)的小型應(yīng)用示例中,我們手動(dòng)編寫(xiě)了reducer。

    React的useReducer與Redux中的reducer之間存在概念上的差異。在React中,我們編寫(xiě)了任意數(shù)量的reducer。它們只是各種便于更新?tīng)顟B(tài)的Hooks。而在Redux中,它們作為概念性的分離,以應(yīng)對(duì)集中式的存儲(chǔ)。

    正如下面代碼段所示,我們可以為ReGraph編寫(xiě)一個(gè)定制的Hook,來(lái)封裝所有需要用到的prop:

    JavaScript

     
     
     
     
    1. import React, { useState, useReducer, useCallback } from 'react'; 
    2.   
    3. import { has, merge, mapValues, isEmpty } from 'lodash'; 
    4. import { chart as defaults } from 'defaults'; 
    5.   
    6. const linkColor = '#fff9c4'; 
    7. const nodeColor = '#FF6D66'; 
    8.   
    9. function isNode(item) { 
    10.   return item.id1 == null && item.id2 == null; 
    11.   
    12. function transformItems(items, itemFn) { 
    13.   return mapValues(items, (item, id) => { 
    14.     const newItem = itemFn(item, id); 
    15.     return newItem ? merge({}, item, newItem) : item 
    16.   }); 
    17. }; 
    18.   
    19. function styleItems(items) { 
    20.   return transformItems(items, item => { 
    21.     return defaults.styles[isNode(item) ? 'node' : 'link']; 
    22.   }); 
    23.   
    24.   
    25. function itemsReducer(items, action) { 
    26.   const newItems = { ...items }; 
    27.   if (action.type === 'SET') { 
    28.     return { ...newItems, ...styleItems(action.newItems) } 
    29.   } 
    30.   else if (action.type === 'REMOVE') { 
    31.     Object.keys(action.removeItems).forEach(removeId => { delete newItems[removeId]; }) 
    32.     return newItems; 
    33.   } else { 
    34.     throw new Error(`No action ${action.type} found`); 
    35.   } 
    36.   
    37. function combineReducer(combine, action) { 
    38.   const newCombine = { ...combine }; 
    39.   if (action.type === 'COMBINE') { 
    40.     newCombine.properties.push(action.property); 
    41.     newCombine.level = combine.level + 1; 
    42.   } 
    43.   else if (action.type === 'UNCOMBINE') { 
    44.     newCombine.properties.pop(); 
    45.     newCombine.level = combine.level - 1; 
    46.   } else { 
    47.     throw new Error(`No action ${action.type} found`); 
    48.   } 
    49.   return newCombine; 
    50.   
    51. function useChart({ initialItems = {} }) { 
    52.   
    53.   const styledItems = styleItems(initialItems) 
    54.   
    55.   const [items, dispatch] = useReducer(itemsReducer, styledItems) 
    56.   const addItems = useCallback(newItems => dispatch({ type: 'SET', newItems }), []) 
    57.   const removeItems = useCallback(removeItems => dispatch({ type: 'REMOVE', removeItems }), []) 
    58.   
    59.   const [combine, combineDispatch] = useReducer(combineReducer, defaults.combine) 
    60.   const combineItems = useCallback(property => combineDispatch({ type: 'COMBINE', property }), []) 
    61.   const uncombineItems = useCallback(property => combineDispatch({ type: 'UNCOMBINE', property }), []) 
    62.   
    63.   const [animation, setAnimation] = useState(defaults.animation); 
    64.   const [view, setView] = useState(defaults.view); 
    65.   
    66.   const [layout, setLayout] = useState(defaults.layout); 
    67.   const [positions, setPositions] = useState(defaults.positions); 
    68.   const [selection, setSelection] = useState(defaults.selection); 
    69.   const [map, setMap] = useState(defaults.map); 
    70.   
    71.   const [options, setOptions] = useState(defaults.options); 
    72.   
    73.   const chartState = { items, options, layout, positions, selection, map, animation, combine } 
    74.   return [chartState, { addItems, removeItems, setPositions, setSelection, combineItems, uncombineItems }] 
    75.   
    76. export { useChart, isNode } 

    值得注意的是,由于ReGraph會(huì)針對(duì)每一個(gè)prop用到大量的useState調(diào)用,因此我們可以將它們放入一個(gè)簡(jiǎn)單的對(duì)象中,并通過(guò)單個(gè)函數(shù)處理,來(lái)實(shí)現(xiàn)更新。為了簡(jiǎn)單起見(jiàn),我們可以使用lodash merge,來(lái)合并條目的更新。同時(shí),在生產(chǎn)環(huán)境中,我們會(huì)使用Immer之類(lèi)的工具,來(lái)提高更新的效率。

    Context API

    我們定制的useChart Hook足以滿(mǎn)足讓單個(gè)組件去控制圖表。但是,我們又該如何處置Sidebar呢?此時(shí),我們就需要通過(guò)全局范圍的Redux來(lái)解決。

    作為React API的一部分,由于Context可以讓各種數(shù)據(jù)在用戶(hù)定義的范圍內(nèi),被訪問(wèn)到,因此它可以協(xié)助我們實(shí)現(xiàn)在Redux中,創(chuàng)建全局存儲(chǔ)。

    雖然,業(yè)界有對(duì)于context是否能成為Redux useContext替代品的爭(zhēng)論,但是有一點(diǎn)可以肯定:它是一種純凈的API,可以在組件之間一致性地共享context。 如下代碼段展示了如何使用Hook和Context:

    JavaScript

     
     
     
     
    1. import React, { useState, useReducer, useCallback } from 'react'; 
    2.   
    3. import merge from 'lodash/merge'; 
    4. import mapValues from 'lodash/mapValues'; 
    5.   
    6. import { chart as defaults } from 'defaults'; 
    7.   
    8. const ChartContext = React.createContext(); 
    9.   
    10. function isNode(item) { 
    11.  return item.id1 == null && item.id2 == null; 
    12.   
    13. function transformItems(items, itemFn) { 
    14.  return mapValues(items, (item, id) => { 
    15.    const newItem = itemFn(item, id); 
    16.    return newItem ? merge({}, item, newItem) : item; 
    17.  }); 
    18.   
    19. function styleItems(items) { 
    20.  return transformItems(items, (item) => { 
    21.    return defaults.styles[isNode(item) ? 'node' : 'link']; 
    22.  }); 
    23.   
    24. function itemsReducer(items, action) { 
    25.  const newItems = { ...items }; 
    26.  if (action.type === 'SET') { 
    27.    return { ...newItems, ...styleItems(action.newItems) }; 
    28.  } else if (action.type === 'REMOVE') { 
    29.    Object.keys(action.removeItems).forEach((removeId) => { 
    30.      delete newItems[removeId]; 
    31.    }); 
    32.    return newItems; 
    33.  } else { 
    34.    throw new Error(`No action ${action.type} found`); 
    35.  } 
    36.   
    37. function combineReducer(combine, action) { 
    38.  const newCombine = { ...combine }; 
    39.  if (action.type === 'COMBINE') { 
    40.    newCombine.properties.push(action.property); 
    41.    newCombine.level = combine.level + 1; 
    42.  } else if (action.type === 'UNCOMBINE') { 
    43.    newCombine.properties.pop(); 
    44.    newCombine.level = combine.level - 1; 
    45.  } else { 
    46.    throw new Error(`No action ${action.type} found`); 
    47.  } 
    48.  return newCombine; 
    49.   
    50. function ChartProvider({ children }) { 
    51.  const [items, dispatch] = useReducer(itemsReducer, {}); 
    52.  const addItems = useCallback((newItems) => dispatch({ type: 'SET', newItems }), []); 
    53.  const removeItems = useCallback((removeItems) => dispatch({ type: 'REMOVE', removeItems }), []); 
    54.   
    55.  const [combine, combineDispatch] = useReducer(combineReducer, defaults.combine); 
    56.  const combineItems = useCallback((property) => combineDispatch({ type: 'COMBINE', property }),[]); 
    57.  const uncombineItems = useCallback((property) => combineDispatch({ type: 'UNCOMBINE', property }),[]); 
    58.   
    59.  const [animation, setAnimation] = useState(defaults.animation); 
    60.  const [view, setView] = useState(defaults.view); 
    61.   
    62.  const [layout, setLayout] = useState(defaults.layout); 
    63.  const [positions, setPositions] = useState(defaults.positions); 
    64.  const [selection, setSelection] = useState(defaults.selection); 
    65.  const [map, setMap] = useState(defaults.map); 
    66.   
    67.  const [options, setOptions] = useState(defaults.options); 
    68.   
    69.   
    70.  const value = [ 
    71.    { view, items, options, layout, positions, selection, map, animation, combine }, 
    72.    { addItems, removeItems, setOptions, setMap, setView, setLayout, setAnimation, setPositions, setSelection, combineItems, uncombineItems }, 
    73.  ]; 
    74.   
    75.  return {children}; 
    76.   
    77. function useChart() { 
    78.  const context = React.useContext(ChartContext); 
    79.  if (context === undefined) { 
    80.    throw new Error('useChart must be used within a ChartProvider'); 
    81.  } 
    82.  return context; 
    83.   
    84. export { ChartProvider, useChart }; 

    下面,我使用定制的ChartProvider上下文,來(lái)包裝那些需要訪問(wèn)圖表的詳細(xì)信息,以及設(shè)置器的任何組件:

    HTML

     
     
     
     
    1.  
    2.      
    3.          
    4.              
    5.              
    6.          
    7.          
    8.      
    9.  

    接著,我們需要通過(guò)如下簡(jiǎn)單的調(diào)用,導(dǎo)入useChart,并獲取當(dāng)前圖表的狀態(tài),以及應(yīng)用層次結(jié)構(gòu)中任意位置的調(diào)度函數(shù)。

     
     
     
     
    1. const  [state, { setLayout }] = useChart(); 

    Context與Redux

    可見(jiàn),使用Context和Redux存儲(chǔ)之間的關(guān)鍵區(qū)別在于,Context必須由您來(lái)定義范圍,而不會(huì)自動(dòng)地為應(yīng)用程序的其余部分提供服務(wù)。這會(huì)迫使我們更加有意識(shí)地去規(guī)劃應(yīng)用程序的邏輯。正如useReducer那樣,我們通常的做法是:創(chuàng)建許多不同的上下文,以供應(yīng)用程序去使用。

    小結(jié)

    綜上所述,我們先介紹了如何使用Redux Toolkit的綜合狀態(tài)管理策略,去實(shí)現(xiàn)全局存儲(chǔ);然后探究了一個(gè)簡(jiǎn)單的應(yīng)用程序,如何通過(guò)使用核心的React Hooks,去實(shí)現(xiàn)狀態(tài)管理的各個(gè)細(xì)節(jié);最后得出兩者可以混合使用,相互補(bǔ)足的使用建議。


    分享題目:ReactHooks和Redux哪個(gè)才是更好的狀態(tài)管理策略?
    當(dāng)前地址:http://m.5511xx.com/article/cocjdpe.html