【全棧React】第19天: 用Redux進行數據管理

本文轉載自:衆成翻譯
譯者:iOSDevLog
連接:http://www.zcfy.cc/article/3811
原文:https://www.fullstackreact.com/30-days-of-react/day-19/css

隨着咱們瞭解了flux和Redux的知識,讓咱們將Redux整合到咱們的應用中,並經過鏈接的應用。react

昨天, 咱們討論了流量模式的緣由, 它是什麼, 咱們有不一樣的選擇, 以及介紹了Reduxnpm

今天, 咱們將回到代碼和添加Redux在咱們的應用。如今咱們正在用它構建的應用是簡單的, 這隻會顯示咱們最後一次獲取當前時間的頁面。爲了簡單起見, 咱們不會調用遠程服務器, 只需使用 JavaScript 的Date 對象。redux

第一件事, 咱們使用redux將不得不安裝redux庫。咱們可使用npm 包管理器來安裝redux。在咱們之前構建的應用的根目錄中, 讓咱們運行npm install 命令來安裝redux:promise

npm install --save redux

想使用redux, 咱們還須要安裝另外一個包, react-redux , 幫助咱們把reactredux綁在一塊兒。瀏覽器

npm install --save react-redux

配置和設置

接下來的工做, 咱們須要作的是在咱們的應用內創建redux。咱們須要執行如下操做才能設置它:服務器

  1. 定義歸約react-router

  2. 建立Storedom

  3. 建立動做創造者ide

  4. Store與咱們的React意見聯繫起來

  5. 得益

第5步沒有promises, 但它會更好, 嗯?

Precursor先驅

咱們少許地討論術語,(讓咱們的手指移動是更重要的)。咱們將只是稍微調整咱們的應用(煩人, 我知道, 但這是最後一次), 因此咱們爲了提供數據經過咱們的應用能夠建立一個包裝組件。

完成後, 咱們的應用樹將具備如下形狀:

[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。咱們將採起的步驟以配合一些咱們將創建的大多數應用的Redux結構一般都是相同的。咱們須要:

  1. 寫一個根歸約器

  2. 寫 actionCreators

  3. 配置存儲、rootReducer和應用

  4. 將視圖鏈接到 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。若是操做的typeFETCH_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模塊組合在一塊兒來工做。

相關文章
相關標籤/搜索