本文轉載自:衆成翻譯
譯者:iOSDevLog
連接:http://www.zcfy.cc/article/3811
原文:https://www.fullstackreact.com/30-days-of-react/day-19/css
隨着咱們瞭解了flux和Redux的知識,讓咱們將Redux整合到咱們的應用中,並經過鏈接的應用。react
昨天, 咱們討論了流量模式的緣由, 它是什麼, 咱們有不一樣的選擇, 以及介紹了Redux。npm
今天, 咱們將回到代碼和添加Redux在咱們的應用。如今咱們正在用它構建的應用是簡單的, 這隻會顯示咱們最後一次獲取當前時間的頁面。爲了簡單起見, 咱們不會調用遠程服務器, 只需使用 JavaScript 的Date
對象。redux
第一件事, 咱們使用redux將不得不安裝redux庫。咱們可使用npm
包管理器來安裝redux
。在咱們之前構建的應用的根目錄中, 讓咱們運行npm install
命令來安裝redux:promise
npm install --save redux
想使用redux, 咱們還須要安裝另外一個包, react-redux
, 幫助咱們把react
和 redux
綁在一塊兒。瀏覽器
npm install --save react-redux
接下來的工做, 咱們須要作的是在咱們的應用內創建redux。咱們須要執行如下操做才能設置它:服務器
定義歸約react-router
建立Storedom
建立動做創造者ide
將Store與咱們的React意見聯繫起來
得益
第5步沒有promises, 但它會更好, 嗯?
咱們少許地討論術語,(讓咱們的手指移動是更重要的)。咱們將只是稍微調整咱們的應用(煩人, 我知道, 但這是最後一次), 因此咱們爲了提供數據經過咱們的應用能夠建立一個包裝組件。
完成後, 咱們的應用樹將具備如下形狀:
[Root] -> [App] -> [Router/Routes] -> [Component]
廢話少說, 讓咱們將咱們的 src/App.js
移動到src/containers
目錄中, 咱們須要同時更新來自咱們的導入的一些路徑。咱們將使用幾天前討論的React路由材料。
咱們將在 <Switch />
語句中包含幾條路由, 以確保一次只顯示一個。
import React from 'react'; import { BrowserRouter as Router, Route, Switch } from 'react-router-dom' // We'll load our views from the `src/views` // directory import Home from './views/Home/Home'; import About from './views/About/About'; const App = props => { return ( <Router> <Switch> <Route path="/about" component={About} /> <Route path="*" component={Home} /> </Switch> </Router> ) } export default App;
此外, 咱們將須要建立一個新的容器, 咱們將調用Root
, 這將包裝咱們的整個 <App />
組件, 並使可用的其他應用。讓咱們建立src/containers/Root.js
文件:
`touch src/containers/Root.js`
目前, 咱們將在這裏使用一個佔位符組件, 但咱們將在討論存儲時替換此內容。如今, 讓咱們導出 _一些東西_:
import React from 'react'; import App from './App'; const Root = (props) => { return ( <App /> ); } export default Root;
最後, 讓咱們更新的路由, 咱們渲染咱們的應用在src/index.js
文件使用咱們的新的Root
容器, 而不是它之前使用的App
。
import React from 'react'; import ReactDOM from 'react-dom'; import Root from './containers/Root'; import './index.css'; ReactDOM.render( <Root />, document.getElementById('root') );
如今有了一個堅實的應用結構就位, 咱們能夠開始添加Redux。咱們將採起的步驟以配合一些咱們將創建的大多數應用的Redux結構一般都是相同的。咱們須要:
寫一個根歸約器
寫 actionCreators
配置存儲、rootReducer和應用
將視圖鏈接到 actionCreators
咱們故意保持這個高層次的介紹短一些, 因此堅持一下, 這一切都將在短時間內變得更有意義。
讓咱們設置容許咱們添加Redux的結構。咱們將在src/redux
目錄中完成幾乎全部的工做。讓咱們建立該目錄。
mkdir -p src/redux touch src/redux/configureStore.js touch src/redux/reducers.js
首先咱們先建立歸約器。雖然它聽起來很複雜, 可是歸約器其實是至關直接的, 有必定的經驗。歸約器僅是 字面 函數。它的惟一責任是返回一個 next 狀態的表示。
在Redux模式, 不像flux, 咱們只在 整個 應用處理 一個 全局存儲的。這使得事情變得更容易處理, 由於有一個地方的數據, 咱們的應用生活。
root 歸約器函數負責返回應用當前全局狀態的表示形式。當咱們在存儲上發送操做時, 將使用應用的當前狀態和致使狀態更新的操做來調用此歸約器函數。
讓咱們在一個文件中創建咱們的根歸約器在src/redux/reducers.js
.。
// Initial (starting) state const initialState = { currentTime: new Date().toString() } // Our root reducer starts with the initial state // and must return a representation of the next state const rootReducer = (state = initialState, action) => { return state; } export default rootReducer
I在函數中, 咱們將第一個參數定義爲初始狀態 (第一次運行時, 不帶參數調用rootReducer
, 所以它老是在第一次運行時返回 initialState
)。
這就是如今的 rootReducer。就像如今這樣, state的價值老是和 initialState 同樣。在咱們的例子中, 這意味着咱們的數據樹有一個單一的currentTime
鍵。
這裏的第二個參數是從存儲區發送的操做。咱們很快就會回來。如今,讓咱們來看看動做。
最起碼, 一個動做 必須 包括一個type
鍵。type
鍵能夠是咱們想要的任何值, 但它必須存在。例如, 在咱們的應用中, 咱們將偶爾發送一項操做, 咱們想告訴存儲獲取 最新 當前時間。咱們能夠將此操做稱爲FETCH_NEW_TIME
的字符串值。
咱們可能從咱們的存儲發送的處理此更新的操做以下:
{ type: 'FETCH_NEW_TIME' }
正如咱們將經過鍵入這個字符串不少, 咱們但願避免可能的拼寫錯誤的地方, 它是常見的建立一個types.js
文件, 將操做類型導出爲常量。讓咱們遵循這個約定, 建立一個src/redux/types.js
文件:
`export const FETCH_NEW_TIME = 'FETCH_NEW_TIME';`
咱們將從types.js
文件中引用它, 而不是使用'FETCH_NEW_TIME' 的硬編碼字符串調用該操做:
import * as types from './types'; { type: types.FETCH_NEW_TIME, }
當咱們想發送數據與咱們的動做, 咱們能夠添加任何鍵, 咱們想咱們的動做。咱們一般會看到這個所謂的payload 有效載荷
, 但它能夠被稱爲任何東西。這是一個公約, 調用附加信息的payload
。
咱們的FETCH_NEW_TIME
動做將發送一個有效載荷與新的當前時間。由於咱們想發送一個 序列化 值與咱們的動做, 咱們將發送新的當前時間的字符串值。
{ type: types.FETCH_NEW_TIME, payload: new Date().toString() // Any serializable value }
回到咱們的歸約器, 咱們能夠檢查的動做類型, 並採起適當的步驟, 建立下一個狀態。在咱們的狀況下, 咱們只儲存payload
。若是操做的type
是FETCH_NEW_TIME
, 咱們將返回新的 currentTime (從咱們的操做有效負載) 和其餘狀態 (使用 ES6 擴展語法):
export const rootReducer = (state = initialState, action) => { switch(action.type) { case types.FETCH_NEW_TIME: return { ...state, currentTime: action.payload} default: return state; } }
請記住, 歸約 必須 返回一個狀態, 因此在默認狀況下, _最起碼_確保返回當前狀態。
保持輕型化
因爲歸約器的功能每次調度一個動做, 咱們要確保這些功能是儘量簡單和快速。咱們不但願他們形成任何反作用或有太多的延遲。
咱們將處理動做創造者中歸約器的反作用。
在咱們看動做創造者 (以及爲何咱們稱他們爲動做創造者) 以前, 讓咱們把咱們的存儲與咱們的應用掛鉤。
咱們將使用 react-redux
包將咱們的視圖鏈接到咱們的redux存儲。讓咱們確保使用npm
安裝此包:
npm install --save react-redux
react-redux
包輸出一個名爲 Provider
的組件。Provider
組件使存儲可使用咱們的應用中的全部容器組件, 而無需咱們每次都須要手動傳遞它。
Provider
組件指望一個store
的屬性, 它指望是一個有效的redux存儲, 因此咱們的應用沒有錯誤運行以前,咱們須要完成一個configureStore
功能,。如今, 讓咱們在應用中鏈接Provider
組件。咱們將經過更新咱們之前建立的包裝Root
組件來使用Provider
組件來完成此項。
import { Provider } from 'react-redux'; // ... const Root = (props) => { // ... return ( <Provider store={store}> <App /> </Provider> ); }
請注意, 咱們正在將store
值發送到Provider
組件, 但咱們尚未建立該存儲。如今咱們來解決這個問題。
爲了建立一個存儲, 咱們將使用新的src/redux/configureStore.js
來導出將負責建立存儲的函數。
咱們如何建立一個存儲?
redux包輸出一個叫作
createStore 的函數, 它將爲咱們建立實際的存儲區, 所以, 讓咱們打開
src/redux/configureStore.js 文件並導出一個稱爲
configureStore()的函數 (咱們將很快定義), 並導入
createStore` 幫助器:
import {createStore} from 'redux'; // ... export const configureStore = () => { // ... } // ... export default configureStore;
實際上咱們在咱們的存儲尚未返回任何東西, 因此讓咱們實際建立的redux
存儲使用 createStore
的功能, 咱們從redux導入:
import {createStore} from 'redux'; export const configureStore = () => { const store = createStore(); return store; } export default configureStore;
若是咱們在瀏覽器中加載咱們的頁面, 咱們將看到咱們有一個巨大的錯誤: 沒有頁面獲得渲染。
redux給咱們的錯誤告訴咱們, 咱們存儲裏沒有歸約器。若是沒有歸約器, 它將不知道如何處理動做或如何建立狀態等。爲了越過這個錯誤, 咱們須要參考咱們建立的 rootReducer。
createStore
函數要求咱們將 rootReducer 做爲第一個參數來傳遞。它還但願將初始狀態做爲第二個參數傳遞。咱們將從咱們建立的reducers.js
文件中導入這兩個值。
import { rootReducer, initialState } from './reducers' // ... export const configureStore = () => { const store = createStore( rootReducer, // root reducer initialState, // our initialState ); return store; }
如今, 讓咱們經過調用 configureStore()
函數建立store
的實例來更新咱們的Root.js
文件。
const Root = (props) => { const store = configureStore(); return ( <Provider store={store}> <App /> </Provider> ); }
咱們的應用中的一切都設置爲使用redux, 而無需 太 多開銷。redux
提供的一個更方便的方法是使用react-redux
包導出的connect()
函數, 將狀態樹的片段 綁定 到不一樣的組件。
connect()
函數返回一個函數, 它指望第一參數是一個組件。這一般叫作高階組件。
connect()
函數要求咱們在函數中至少傳遞一個參數 (但一般咱們會傳遞兩個)。它所指望的第一個參數是一個函數, 它將被稱爲state
並指望一個返回的對象將數據鏈接到視圖。讓咱們看看咱們是否能在代碼中揭開這種行爲的神祕面紗。
咱們將這個函數稱爲mapStateToProps
函數。由於它的責任是將狀態映射到與組件的原始props
合併的對象。
讓咱們在src/views/Home.js
中建立 home 視圖, 並使用此connect()
函數來綁定currentTime
在咱們的狀態樹中的值。
import { connect } from 'react-redux'; // ... const mapStateToProps = state => { return { currentTime: state.currentTime } } export default connect( mapStateToProps )(Home);
此connect()
函數 自動 將函數的第一個參數中的任何鍵傳遞爲Home
組件的props
。
在咱們的演示案例中, Home
組件中的currentTime
屬性將映射到currentTime
中的狀態樹鍵。讓咱們更新 Home
組件, 以顯示currentTime
中的值:
const Home = (props) => { return ( <div className="home"> <h1>Welcome home!</h1> <p>Current time: {props.currentTime}</p> </div> ); }
雖然這個演示不是頗有趣, 它代表咱們有咱們的Redux
應用創建了咱們的 data
致力於全局狀態和咱們的視圖組件映射數據。
明天, 咱們將開始經過操做建立者來觸發咱們的全局狀態的更新, 並經過將多個redux模塊組合在一塊兒來工做。