精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

React Hooks和Redux哪個才是更好的狀態(tài)管理策略?

譯文
開發(fā) 前端
本文先介紹了如何使用Redux Toolkit的綜合狀態(tài)管理策略,去實現(xiàn)全局存儲;然后探究了一個簡單的應(yīng)用程序,如何通過使用核心的React Hooks,去實現(xiàn)狀態(tài)管理的各個細(xì)節(jié);最后得出兩者可以混合使用,相互補(bǔ)足的使用建議。

[[426178]]

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

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

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

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

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

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

如上圖所示,我們在此所遵循的原則為:

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

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

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

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

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

Redux狀態(tài)管理

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

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

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

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

讓我們來考慮一個標(biāo)準(zhǔn)化的圖形可視化用例:在圖表中添加和刪除節(jié)點。為了在全局存儲中創(chuàng)建一個狀態(tài)“切片”,我們在store.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. }); 

為了讓應(yīng)用程序中的其他組件能夠訪問該存儲,我們可以對應(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.  <React.StrictMode> 
  11.    <Provider store={store}> 
  12.      <App /> 
  13.    </Provider> 
  14.  </React.StrictMode>, 
  15.  document.getElementById('root'
  16. ); 

其中的Provider段意味著,其任何下游都可以訪問該存儲。在itemsSlice.js中,我們?yōu)楦鱾€條目定義了狀態(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; 

通過上述代碼段,我們可以獲悉:

  • ReGraph的條目是各種通過ID索引的節(jié)點和鏈接對象。其核心數(shù)據(jù)結(jié)構(gòu)十分常見。Redux Toolkit會通過一些輔助函數(shù),來處理此類格式數(shù)據(jù)。在此,我們用到了由createEntityAdapter提供的addMany、addOne、removeMany、以及removeOne等功能。
  • 在Redux中,Selector允許我們從存儲中獲取一個狀態(tài)片。我可以利用getSelectors適配器,來避免自行編寫狀態(tài)查詢代碼。
  • 最后,我們導(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.    <div className={styles.container}> 
  44.      <Chart 
  45.        items={styledItems} 
  46.        animation={{ animate: false }} 
  47.        options={{ backgroundColor: 'rgba(0,0,0,0)', navigation: false, overview: false }} 
  48.      /> 
  49.      <div className={styles.row}> 
  50.        <button 
  51.          className={styles.button} 
  52.          aria-label="Add items" 
  53.          onClick={() => dispatch(addItem({ id: itemCount + 1, type: 'user' }))} 
  54.        > 
  55.          Add User 
  56.        </button> 
  57.        <button 
  58.          className={styles.button} 
  59.          aria-label="Remove Items" 
  60.          onClick={() => dispatch(removeItem(itemCount))} 
  61.        > 
  62.          Remove User 
  63.        </button> 
  64.      </div> 
  65.    </div> 
  66.  ); 

通過使用Redux Hook的suseSelector,我們可以輕松利用切片代碼,來提供選擇器。同時,其useDispatch允許我們根據(jù)狀態(tài)的“調(diào)度(dispatch)”動作(Redux的另一個實用部分),去變更狀態(tài)。

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

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

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

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

Prop Drilling

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

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

React Hooks

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

JavaScript

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

對于使用過Redux的人來說,Hook的useReducer以及如下代碼段,一定不會陌生。

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; 

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

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

正如下面代碼段所示,我們可以為ReGraph編寫一個定制的Hook,來封裝所有需要用到的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會針對每一個prop用到大量的useState調(diào)用,因此我們可以將它們放入一個簡單的對象中,并通過單個函數(shù)處理,來實現(xiàn)更新。為了簡單起見,我們可以使用lodash merge,來合并條目的更新。同時,在生產(chǎn)環(huán)境中,我們會使用Immer之類的工具,來提高更新的效率。

Context API

我們定制的useChart Hook足以滿足讓單個組件去控制圖表。但是,我們又該如何處置Sidebar呢?此時,我們就需要通過全局范圍的Redux來解決。

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

雖然,業(yè)界有對于context是否能成為Redux useContext替代品的爭論,但是有一點可以肯定:它是一種純凈的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 <ChartContext.Provider value={value}>{children}</ChartContext.Provider>; 
  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上下文,來包裝那些需要訪問圖表的詳細(xì)信息,以及設(shè)置器的任何組件:

HTML

  1. <App> 
  2.     <ChartProvider> 
  3.         <VisualizationContainer> 
  4.             <Chart/> 
  5.             <Timeline/> 
  6.         </VisualizationContainer> 
  7.         <Sidebar/> 
  8.     </ChartProvider> 
  9. </App> 

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

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

Context與Redux

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

小結(jié)

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

原文標(biāo)題:React Hooks vs. Redux: Choosing the Right State Management Strategy ,作者:Christian Miles

【51CTO譯稿,合作站點轉(zhuǎn)載請注明原文譯者和出處為51CTO.com】

 

責(zé)任編輯:華軒 來源: 51CTO
相關(guān)推薦

2021-08-14 08:45:27

React開發(fā)應(yīng)用程序

2024-04-22 09:12:39

Redux開源React

2021-11-05 10:36:19

性能優(yōu)化實踐

2021-07-26 09:00:08

ReactHooks 項目

2019-03-13 10:10:26

React組件前端

2024-04-25 09:10:50

ReactReduxJavaScript

2019-08-20 15:16:26

Reacthooks前端

2023-11-06 08:00:00

ReactJavaScript開發(fā)

2022-03-18 14:09:52

ReactJavaScript

2024-01-08 09:36:47

管理庫代碼

2020-10-28 09:12:48

React架構(gòu)Hooks

2021-12-17 19:15:51

前端蟲洞狀態(tài)

2017-09-08 13:35:48

云優(yōu)先策略互聯(lián)網(wǎng)

2021-03-18 08:00:55

組件Hooks React

2022-04-21 08:01:34

React框架action

2022-03-31 17:54:29

ReactHooks前端

2021-06-28 11:17:14

CoutPrintf接口

2017-03-02 14:52:46

2020-09-19 17:46:20

React Hooks開發(fā)函數(shù)

2024-02-05 21:48:25

VueReactHooks
點贊
收藏

51CTO技術(shù)棧公眾號

免费精品一区| 在线看黄色av| 亚洲综合三区| 在线电影欧美日韩一区二区私密| 国产精品永久免费观看| 一二三四国产精品| 亚洲精品观看| 色狠狠色狠狠综合| 好色先生视频污| 人妻无码中文字幕| 免费高清视频精品| 欧美国产日韩一区二区在线观看| 中国黄色a级片| 免费视频成人| 黄色精品在线看| 亚洲国产精品123| 午夜美女福利视频| 日韩激情中文字幕| 欧美激情手机在线视频 | 欧美一区二区三区四区久久 | 男人的天堂亚洲| 日韩小视频在线观看| www.四虎在线| 97久久中文字幕| 一本一道久久a久久精品| 粉嫩av一区二区三区天美传媒| 欧美香蕉爽爽人人爽| 国产在线麻豆精品观看| 国产不卡在线观看| 天堂网一区二区三区| 欧美顶级大胆免费视频| 电影在线观看一区| 成人综合婷婷国产精品久久 | 中文字幕中文在线| 国产伦子伦对白在线播放观看| 国产精品高清亚洲| 国产精品久久久久久久天堂| 九九热国产精品视频| 日韩在线看片| 亚洲性av在线| 97人妻精品一区二区免费| 国产精品1luya在线播放| 91精品国产入口在线| 成人性生交免费看| 福利视频一区| 在线免费不卡电影| 日本男人操女人| 不卡av播放| 日韩欧美国产免费播放| 浮妇高潮喷白浆视频| av电影院在线看| 亚洲一区二区欧美激情| 91精品一区二区三区四区| 精产国品自在线www| 国产精品视频yy9299一区| 色综合666| 国产天堂在线| 国产日韩欧美不卡| 亚洲福利av| 日本中文字幕在线播放| 国产精品国产三级国产| 在线国产精品网| 欧美性天天影视| 国产精品久久久久久久久快鸭| 天堂精品视频| 麻豆网在线观看| 亚洲人123区| 美女av免费观看| 黄色美女视频在线观看| 亚洲福利一区二区三区| 激情伊人五月天| 在线视频cao| 欧美综合一区二区三区| 中文字幕第36页| 日韩成人免费av| 欧美一二区视频| yjizz视频| 一区二区三区视频免费观看 | 视频免费一区| 亚洲人吸女人奶水| 免费网站在线观看视频| 深夜福利在线看| 久久久久欧美精品| 国产精品麻豆va在线播放| 亚洲视频久久久| 国产剧情一区二区| 精品国产免费久久久久久尖叫| 外国精品视频在线观看 | 成人国产一区二区| 日本一二三区在线视频| 国产精品久久久99| 妺妺窝人体色www看人体| 国产精品论坛| 欧美日韩精品系列| av不卡中文字幕| 久久综合欧美| 亚洲第一精品夜夜躁人人爽| av直播在线观看| 91九色精品| 7777kkkk成人观看| 国产一区二区三区成人| 久久久夜精品| 91久久久久久久久久久| 人人妻人人澡人人爽久久av | 丝袜诱惑亚洲看片| 91欧美激情另类亚洲| 日本在线丨区| 亚洲一区二区三区影院| 看欧美ab黄色大片视频免费| 亚洲综合影院| 中文字幕欧美亚洲| 日韩欧美不卡视频| 国内一区二区视频| 日产精品高清视频免费| 黑人精品视频| 欧美精品aⅴ在线视频| 久久久久9999| 午夜久久影院| 伦伦影院午夜日韩欧美限制| 国产精品视频免费播放| 国产一区二区日韩精品| 欧洲在线视频一区| 白白色在线观看| 欧美日本免费一区二区三区| 扒开jk护士狂揉免费| 欧美日韩国产探花| 欧美精品久久久久久久久| 国产精品成人久久久| gogogo免费视频观看亚洲一| 欧美h视频在线观看| 国产精品久久久久久福利| 亚洲不卡av一区二区三区| 日本黄大片在线观看| 国产美女久久| 国产午夜精品视频免费不卡69堂| 日韩av电影网| 国产成人av一区二区三区在线观看| 亚洲国产一区二区精品视频| 午夜精品成人av| 日韩麻豆第一页| 国产亚洲欧美精品久久久www| 精品无人码麻豆乱码1区2区 | 国产成人精品综合久久久| 欧日韩在线视频| 亚洲一区二区三区视频在线播放| 亚洲色图欧美制服丝袜另类第一页| 日日摸天天爽天天爽视频| 7m精品国产导航在线| 久久这里有精品| 伊人成人在线观看| 欧美激情综合五月色丁香小说| 欧美日韩精品免费看| 精品丝袜在线| 亚洲精品福利免费在线观看| 亚洲性猛交xxxx乱大交| 精品久久久中文字幕| 538国产精品一区二区在线| 色屁屁草草影院ccyycom| 亚洲国产日韩在线一区模特| 蜜桃色一区二区三区| 欧美激情成人在线| 99久久99| xxxcom在线观看| 亚洲精品一区二区三区蜜桃下载| 蜜桃精品成人影片| 国产模特精品视频久久久久| 欧美国产一二三区| 亚洲成人短视频| 国产亚洲视频中文字幕视频| 最新在线中文字幕| 亚洲视频每日更新| 国产人妖在线观看| 在线日韩av| 欧美久久久久久久| 韩国精品视频在线观看| 久久久成人精品| 亚洲高清精品视频| 狠狠躁夜夜躁人人爽超碰91| 91免费视频污| 综合一区在线| 国产自产精品| 肉色欧美久久久久久久免费看| 亚洲片在线资源| 在线观看色网站| 一区二区三区欧美激情| 欧美一区二区免费在线观看| 久久久成人网| 自拍另类欧美| 美女呻吟一区| 国产精品免费一区二区三区都可以| aaa日本高清在线播放免费观看| 3d成人动漫网站| xxx在线播放| 精品一区二区三区在线播放视频 | 欧美黄色免费| 欧洲精品久久| 日本在线一区二区三区| 欧美有码在线观看视频| 久草中文在线| 精品中文视频在线| 国产精品无码一区二区桃花视频 | 销魂美女一区二区三区视频在线| 日韩尤物视频| 国产欧美一区二区三区米奇| 国产精品欧美在线| а√天堂8资源在线| 这里只有精品在线播放| 日本黄色三级视频| 欧美日韩另类一区| 亚洲 欧美 视频| 亚洲色图一区二区| 偷拍女澡堂一区二区三区| 国内精品在线播放| 亚洲熟妇av一区二区三区| 你懂的国产精品永久在线| 欧美一区二区视频在线| 99久久人爽人人添人人澡| 国产精品日韩av| 欧美xxxhd| 欧美噜噜久久久xxx| 成人在线免费电影| 日韩精品在线免费观看视频| 一卡二卡在线观看| 色狠狠色狠狠综合| 91香蕉在线视频| 亚洲另类春色国产| 亚洲色图第四色| 久久久亚洲精品一区二区三区| www.好吊操| 国产伦精品一区二区三区免费优势| 国产aⅴ夜夜欢一区二区三区 | 国产精品一区二区免费视频| 色综合中文字幕国产| 国产精彩视频在线观看| 波多野结衣91| 69久久精品无码一区二区| 激情亚洲成人| 日本一本草久p| 欧美肥老太太性生活| 五月婷婷一区| 九九热线有精品视频99| 激情视频在线观看一区二区三区| 亚洲视频精选| av在线不卡观看| 午夜视频在线观看精品中文| 成人在线国产精品| 亚洲91在线| 国产精品自拍偷拍| 久久亚洲国产精品尤物| 国产精品91视频| 日韩精品无码一区二区| 综合一区av| 久久视频免费在线| 午夜影院欧美| 热这里只有精品| 我不卡伦不卡影院| 91麻豆天美传媒在线| 亚洲91精品| 2021国产视频| 欧美色图首页| aa视频在线播放| 99精品视频网| 日韩a在线播放| 奇米色一区二区三区四区| 日日噜噜夜夜狠狠| 国内成+人亚洲+欧美+综合在线| 国产精品久久久久久9999| 国产精品一区在线观看乱码| 欧美一级片在线免费观看| 成人国产电影网| 久久精品国产亚洲AV熟女| 国产欧美精品一区| 久久精品日韩无码| 一区二区高清免费观看影视大全| 精品无码人妻一区二区三| 亚洲444eee在线观看| 国产91国语对白在线| 国产精品久线观看视频| 伊人在线视频观看| 亚洲一级二级三级在线免费观看| 日韩久久精品视频| 91久久免费观看| 国产理论片在线观看| 精品粉嫩超白一线天av| 欧美偷拍视频| 俺去亚洲欧洲欧美日韩| heyzo一区| 国产精品久久久久久影视| 国产视频网站一区二区三区| 国产精品swag| 日韩欧美高清在线播放| 成人黄色片免费| 久久国产精品亚洲77777| 亚洲欧美日本一区二区三区| www.欧美色图| 亚洲AV成人无码网站天堂久久| 亚洲国产精品自拍| 中文字幕无线码一区| 亚洲精品一区二区三区在线观看| 成人高清免费在线播放| 欧美高清电影在线看| 日韩影片中文字幕| 国产精品久久波多野结衣| 中文字幕成人| 国产在线一区二区三区欧美| 日韩av密桃| 国产精品333| 国产老女人精品毛片久久| xxxx日本黄色| 午夜影院在线观看欧美| 91久久精品国产91性色69 | 亚洲精品20p| 99re热视频精品| 污软件在线观看| 欧美综合色免费| 亚洲区小说区图片区| 操日韩av在线电影| 福利一区视频| 精品一区久久久久久| 午夜国产欧美理论在线播放| youjizzxxxx18| 久久久91精品国产一区二区三区| 真实国产乱子伦对白在线| 欧美日韩色一区| 蜜桃视频在线观看网站| 国内外成人免费激情在线视频网站| av国产精品| 特级西西444www大精品视频| 免费在线成人| 波多野结衣有码| 亚洲国产色一区| 亚洲av无码一区二区三区性色| 日韩在线观看av| 国产资源一区| 日韩欧美亚洲日产国产| 久久午夜精品| 朝桐光av一区二区三区| 亚洲图片一区二区| 性生活三级视频| 欧美人在线视频| 久久免费精品| 国产一二三四区在线观看| 蜜桃传媒麻豆第一区在线观看| 五月婷婷综合在线观看| 亚洲3atv精品一区二区三区| 人人妻人人澡人人爽精品日本| 欧美风情在线观看| 国产厕拍一区| 99在线免费视频观看| 成人福利视频在线看| 黄网站免费在线| 亚洲电影av在线| 色爱综合区网| 国产精品二区二区三区| 亚洲电影在线| 亚洲调教欧美在线| 黑人巨大精品欧美一区二区三区| 亚洲欧美自偷自拍| 日本精品视频在线观看| 国内精品久久久久久99蜜桃| 中文字幕一区二区三区四区五区六区 | 国产精品一区二区三区乱码| 中文字幕av久久爽av| 精品伦理精品一区| 国产三级电影在线播放| 欧美日韩一区二区视频在线观看| 日韩中文字幕av电影| 91视频免费看片| 欧美一区二区视频观看视频| 欧美性受ⅹ╳╳╳黑人a性爽| 欧美夜福利tv在线| 欧洲专线二区三区| 视频在线观看免费高清| 亚洲欧美另类综合偷拍| 亚洲欧美国产高清va在线播放| 91av在线播放视频| 少妇一区二区视频| 99日在线视频| 亚洲国产一二三| 久久久久久久久福利| 亚洲美女91| 伊人网伊人影院| 欧美二区乱c少妇| 精品一性一色一乱农村| 久久一区二区三区av| 麻豆91精品91久久久的内涵| a级一a一级在线观看| 色婷婷综合久色| www.在线视频| 免费不卡亚洲欧美| 精品在线一区二区| 日韩精品在线不卡| 日韩一区av在线| 精品五月天堂| 人人干人人干人人| 亚洲成人黄色影院| av中文字幕在线| 精品视频一区二区三区四区| 久久精品国产99国产精品| 精品成人免费视频|