3

在我的React應用程序中,我需要存儲一些需要在任何地方都可訪問的數據,但不希望將其保存在全局(即靜態)數據結構中,以便模擬我的單元測試和風格指南。如何避免React應用程序中的全局狀態?

我的意思的數據是例如:

  • 恆定IN_BROWSER即在瀏覽器true構建和在節點false
  • 是在瀏覽器上的啓動初始化恆定IS_MOBILE誰是當前登錄的用戶的
  • 數據,
  • 網址,我連接到API(這是本地主機,中間服務器或生產,取決於配置)

現在我有一個名爲sessionData.js存儲此數據文件,每當我需要的,我在我的代碼做require('./sessionData')並使用它。我可以使用rewire爲我的單元測試嘲笑它,並且,當它們按順序運行時,它現在可以正常工作。這對於s​​tyleguide是有問題的,因爲每個例子都可以從不同用戶的角度展示視圖(理想情況下,每個例子都有自己的sessionData)。

IN_BROWSER目前是global,這也是一個壞主意,因爲它會對來自每個模塊的初始化代碼隱式依賴。

我發現我可以通過道具將我的sessionData和其他東西傳遞給層次結構中的每個組件,但這看起來像是很多冗餘。

有什麼設計模式可以更好地解決它嗎?

回答

1

我使用Webpack,並有一個名爲DefinePlugin的插件來處理這個給我。 我想這是一個很好的做法,因爲它是我放置大部分應用程序配置細節的地方。

例如,我的WebPack配置有:

new webpack.DefinePlugin({ 
    'process.env': {'NODE_ENV': JSON.stringify(options.env)} 
}), 

這樣我就可以在前端使用process.env.NODE_ENV在後端。

我開源我的WebPack配置在這裏:https://github.com/agendor/react-webpack-sample/blob/master/webpack.config.js

這裏是一個link for the plugin documentation

單元測試,我使用了一個test-helper.js類定義了一些全局變量和我需要它在每一個測試開始。我不確定這是否是一種好的做法,但它對我們的項目來說工作得很好。我們運行沒有webpack的測試。也許更好的做法是爲處理某些全局變量的測試設置特定的webpack配置。

+0

這一個是好的,但它有助於只針對第一種情況下,該'IN_BROWSER'不變。 – mik01aj

+0

@ m01我以爲你可以在指向你的'sessionData.js'模塊的插件中添加另一個參數,這樣你就可以不需要每次都使用它。但是你將不得不返回一個函數而不是一個構造對象。不知道它是否合適。 – Tulio

+0

sessionData的問題是我不希望它是全局的。我想爲每個單元測試或每個styleguide示例動態更改它。 – mik01aj

1

如果我正確理解你,爲什麼不通過道具將數據結構傳遞給所有可能需要它的組件?顯然,你不能修改通過的道具,但該數據結構中的你總是可以包含一個回調函數來更新它,用什麼樣的結構:

structure = { 
    dataItem1: stuff, 
    dataItem2: stuff2, 
    dataItemCallback: { var foo = stuff here; } 
} 

然後,您可以撥打此類似:

this.props.structure.dataItem1; 
this.props.structure.dataItem2; 

要更新的東西,你可以隨時撥打:

this.props.structure.dataItemCallback(newData); 

另外,如果你將其穿過的道具,現在所有的組件可如果重新描繪任何結構的變化。

+0

這似乎是個好主意,但後來我擔心結構會變得越來越大(因此難以模擬或維護)。你能否詳細說明如何保持簡單和可維護性? – mik01aj

+0

我不得不說,如果視圖不得不擔心這個結構,那麼你的視圖組件可能太複雜了。在視圖層面,組件應該只是獲取道具並渲染它們而沒有任何邏輯。視圖控制器可能有一些額外的邏輯來處理所有這些數據結構的所有部分到達的位置,並將它們單獨通過道具傳遞給啞視圖組件。 至於如何使結構簡單和可維護?所有取決於你的個人需求,我想:) – Bruce

0

在某些情況下,可以重構組件以直接傳遞所需的數據,而不是將其置於全局狀態。這並不能解決問題,但可以顯着降低問題。這裏有一個nice explanation from Dan Abramov

enter image description here