React Context API: 輕鬆管理狀態

原文連接:React Context API: Managing State with Easecss

譯者:OFEDhtml

使用最新的 React Context API 管理狀態很是容易。如今就跟隨我一塊兒學習下它和 Redux 的區別以及它是如何使用的吧。前端

綜述:React Context API 在 React 生態系統中並非個新鮮事物。不過,在 React 16.3.0 版本中作了一些改進。這些改進是如此巨大,以致於大大減小了咱們對 Redux 和其餘高級狀態管理庫的需求。在本文中,你將經過一個實用教程瞭解到新的 React Context API 是如何取代 Redux 完成小型應用的狀態管理的。node

Redux 快速回顧

在直奔主題以前,咱們先來快速回顧下 Redux,以便咱們更好的比較二者的區別。redux 是一個便於狀態管理的 JavaScript 庫。Redux 自己和 React 並無關係。來自世界各地的衆多開發者選擇在流行的前端框架(好比 ReactAngular )中使用 Redux。react

說明一點,在本文中,狀態管理指的是處理單頁面應用(SPA)中產生的基於特定事件而觸發的狀態變化。好比,一個按鈕的點擊事件或者一條來自服務器的異步信息等,均可以觸發應用狀態的變化。git

在 Redux 中,你尤爲須要注意下面幾點:github

  1. 整個 app 的狀態存儲在單個對象中(該對象被稱做數據源)。
  2. 若是要改變狀態,你須要經過 dispatch 方法觸發 actions,actions 描述了應該發生的事情。
  3. 在 Redux 中,你不能更改對象的屬性或更改現有數組,必須始終返回新對象或新數組。

若是你對 Redux 並不熟悉而且你想要了解更多,請移步 Redux 的實用教程學習。npm

React Context API 指南

The React Context API 提供了一種經過組件樹傳遞數據的方法,而沒必要經過 props 屬性一層層的傳遞。在 React 中,數據一般會做爲一個屬性從父組件傳遞到子組件。json

使用最新的 React Context API 須要三個關鍵步驟:redux

  1. 將初始狀態傳遞給 React.createContext。這個方法會返回一個帶有 ProviderConsumer 的對象。
  2. 使用 Provider 組件包裹在組件樹的最外層,並接收一個 value 屬性。value 屬性能夠是任何值。
  3. 使用 Consumer 組件,在組件樹中 Provider 組件內部的任何地方都能獲取到狀態的子集。

如你所見,所涉及的概念實際上與 Redux 沒有什麼不一樣。事實上,甚至 Redux 也在其公共 API 的底層使用了 React Context API。然而,直到最近,Context API 才達到了足夠成熟的水平。

使用 Redux 建立 React 應用

如上所述,本文的目標是向你展現新的 Context API 如何在小型應用中替代 Redux。所以,你首先要用 Redux 建立一個簡單的 React app,而後,你將學習如何刪除這個狀態管理庫,以便更好地利用 React Context API。

你將構建的示例應用是一個處理一些流行食物及其來源的列表。這個應用還將包括一個搜索功能,使用戶可以根據一些關鍵詞過濾列表。

最終,你將建立一個相似下面所述的應用:

項目要求

因爲本文僅使用 React 和一些 NPM 庫,所以除了 Node.js 和 NPM 以外,你什麼都不須要。若是你尚未安裝 Node.js 和 NPM,請前往官網下載並安裝。

安裝這些依賴後,你還須要安裝 create-react-app 工具。這個工具幫助開發人員建立 React 項目。打開一個終端並運行如下命令來安裝:

npm i -g create-react-app
複製代碼

搭建 React 應用

安裝完 create-react-app 後,進入項目所在目錄,執行如下命令:

create-react-app redux-vs-context
複製代碼

幾秒鐘後,create-react-app 將完成應用程序的建立。在此以後,進入該工具建立的新目錄,並安裝 Redux:

# 進入應用目錄
cd redux-vs-context

# 安裝 Redux
npm i --save redux react-redux
複製代碼

注意: redux 是主庫,react-redux 是促進 React 和 Redux 之間交互的庫。簡而言之,後者充當 React 和 Redux 之間的代理。

使用 Redux 開發 React 應用

你已經搭建好了你的 React 應用,安裝好了 Redux,如今,在你喜歡的開發工具中打開你的項目。而後在 src 文件夾中建立三個文件:

  • foods.json :此文件包含一個用於保存食物及其來源信息的靜態數組
  • reducers.js:此文件用於管理應用中 Redux 狀態
  • actions.js:此文件用於保存應用中觸發 Redux 狀態改變的方法

因此,首先,打開 foods.json 文件,添加以下內容:

[
  {
    "name": "Chinese Rice",
    "origin": "China",
    "continent": "Asia"
  },
  {
    "name": "Amala",
    "origin": "Nigeria",
    "continent": "Africa"
  },
  {
    "name": "Banku",
    "origin": "Ghana",
    "continent": "Africa"
  },
  {
    "name": "Pão de Queijo",
    "origin": "Brazil",
    "continent": "South America"
  },
  {
    "name": "Ewa Agoyin",
    "origin": "Nigeria",
    "continent": "Africa"
  }
]
複製代碼

如你所見,文件存儲的數據並無什麼特別。僅僅是一個包含着不一樣國家不一樣食物的數組。

在定義了 foods.json 文件後,你能夠專一於建立你的 Redux store 了。回顧一下,store 是保存你的應用真實狀態的惟一來源。打開你的 reducers.js 文件,添加如下代碼:

import Food from './foods';

const initialState = {
  food: Food,
  searchTerm: '',
};

export default function reducer(state = initialState, action) {
  // 根據 action type 區分
  switch (action.type) {
    case 'SEARCH_INPUT_CHANGED':
      const {searchTerm} = action.payload;
      return {
        ...state,
        searchTerm: searchTerm,
        food: searchTerm ? Food.filter(
          (food) => (food.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1)
        ) : Food,
      };
    default:
      return state;
  }
}
複製代碼

在上面的代碼中,你能夠看到 reducer 方法接收兩個參數:stateaction。當你啓動你的 React 應用,這個方法將得到它以前定義的 initialState,當你 dispatch 一個 action 的實例時,這個方法將得到當前狀態(再也不是 initialState)。而後,基於這些 actions 的內容,reducer 方法將爲你的應用生成一個新的狀態。

接下來,你須要定義這些 actions 作什麼。實際上,爲了簡單起見,你將定義一個單一的 action ,當用戶在你的應用中輸入搜索詞時,這個 action 會被觸發。所以,打開 actions.js 文件,並在其中插入如下代碼:

function searchTermChanged(searchTerm) {
  return {
    type: 'SEARCH_INPUT_CHANGED',
    payload: {searchTerm},
  };
}

export default {
  searchTermChanged,
};
複製代碼

action 建立好以後,你須要作的下一件事就是將你的 App 組件包裝到 react-redux 提供的 Provider 組件中。Provider 統一負責 React 應用的數據(即 store)傳遞。

要使用 provider ,首先,你將使用 reducers.js 中定義的 initialState 建立 store。而後,經過 Provider 組件,你將把 store 傳給你的 App。要完成這些任務,你必須打開 index.js 文件,並將其內容替換爲:

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import reducers from './reducers';
import App from './App';

// 使用 reducers 信息建立 store。
// 這是由於 reducers 是 Redux Store 的控制中心。
const store = createStore(reducers);

ReactDOM.render(
  <Provider store={store}> <App/> </Provider>,
  document.getElementById('root')
);
複製代碼

就是這樣!你剛剛在 React 應用中配置完 Redux。如今,你必須實現 UI (用戶界面),這樣你的用戶就可使用本節中實現的功能了。

建立 React 界面

如今,你已經完成了應用中的核心代碼,你能夠專一於構建你的用戶界面。爲此,打開你的 App.js 文件,用下方代碼替換它的內容:

import React from 'react';
import {connect} from 'react-redux';
import actions from './actions';
import './App.css';

function App({food, searchTerm, searchTermChanged}) {
  return (
    <div> <div className="search"> <input type="text" name="search" placeholder="Search" value={searchTerm} onChange={e => searchTermChanged(e.target.value)} /> </div> <table> <thead> <tr> <th>Name</th> <th>Origin</th> <th>Continent</th> </tr> </thead> <tbody> {food.map(theFood => ( <tr key={theFood.name}> <td>{theFood.name}</td> <td>{theFood.origin}</td> <td>{theFood.continent}</td> </tr> ))} </tbody> </table> </div> ); } export default connect(store => store, actions)(App); 複製代碼

對於未用過 Redux 的人來講,他們惟一不熟悉的是用於封裝 App 組件的 connect 方法。這個方法其實是一個高階組件( HOC ),充當應用程序和 Redux 之間的粘合劑。

使用如下命令啓動你的應用,你將可以在瀏覽器中訪問你的應用:

npm run start

複製代碼

然而,正如你所看到的,這個應用如今很難看。所以,爲了讓它看起來更好一點,你能夠打開 App.css 文件,用如下內容替換它的內容:

table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 15px;
  line-height: 25px;
}

th {
  background-color: #eee;
}

td, th {
  text-align: center;
}

td:first-child {
  text-align: left;
}

input {
  min-width: 300px;
  border: 1px solid #999;
  border-radius: 2px;
  line-height: 25px;
}

複製代碼

Alt text

完成了!你如今有了一個基本的 React 和 Redux 的應用,能夠開始學習如何遷移到 Context API 上了。

使用 React Context API 實現應用

在本節,你將要學習如何將你的 Redux 應用遷移到 React Context API 上。

幸運的是,你不須要在 Redux 和 Context API 之間作不少的重構。

首先,你必須從你的應用中移除 Redux 組件。爲此,請打開終端,刪除 reduxreact-redux 庫:

npm rm redux react-redux

複製代碼

以後,刪除應用中對這些庫的引用代碼。打開 App.js 刪除如下幾行:

import {connect} from 'react-redux';
import actions from './actions';

複製代碼

而後,在相同的文件中,用下方的代碼替換最後一行(以 export default 開頭的那一行):

export default App;

複製代碼

經過這些改變,你能夠用 Context API 重寫你的應用了。

從 Redux 遷移到 React Context API

要將以前的應用從 Redux 驅動的應用轉換爲使用 Context API,你須要一個 context 來存儲應用的數據(該 context 將替換 Redux Store)。此外,你還須要一個 Context.Provider 組件,該組件包含 stateprops 和正常的 React 組件生命週期。

爲此,你須要在 src 目錄中建立一個 providers.js 文件,並向其中添加如下代碼:

import React from 'react';
import Food from './foods';

const DEFAULT_STATE = { allFood: Food, searchTerm: '' };

export const ThemeContext = React.createContext(DEFAULT_STATE);

export default class Provider extends React.Component {
  state = DEFAULT_STATE;
  searchTermChanged = searchTerm => {
    this.setState({searchTerm});
  };

  render() {
    return (
      <ThemeContext.Provider value={{ ...this.state, searchTermChanged: this.searchTermChanged, }}> {this.props.children} </ThemeContext.Provider>); } } 複製代碼

上面代碼中定義的 Provider 類負責將其餘組件封裝在 ThemeContext.Provider 中。經過這樣作,你可讓這些組件訪問應用中的 state 和更改 state 的 searchTermChanged 方法。

若要在組件樹中使用這些值,你須要建立一個 ThemeContext.Consumer 組件。這個組件將須要一個 render 渲染方法,該方法將接收上述 props 值做爲參數。

所以,接下來,你須要在 src 目錄中建立一個名爲 consumer.js 的文件,並將如下代碼寫入其中:

import React from 'react';
import {ThemeContext} from './providers';

export default class Consumer extends React.Component {
  render() {
    const {children} = this.props;

    return (
      <ThemeContext.Consumer> {({allFood, searchTerm, searchTermChanged}) => { const food = searchTerm ? allFood.filter( food => food.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1 ) : allFood; return React.Children.map(children, child => React.cloneElement(child, { food, searchTerm, searchTermChanged, }) ); }} </ThemeContext.Consumer> ); } } 複製代碼

如今,爲了完成遷移,你將打開 index.js 文件,並在 render() 函數中,用 Consumer 組件包裝 App 組件。此外,你須要將 Consumer 包裝在 Provider 組件中。代碼以下所示:

import React from 'react';
import ReactDOM from 'react-dom';
import Provider from './providers';
import Consumer from './consumer';
import App from './App';

ReactDOM.render(
  <Provider> <Consumer> <App /> </Consumer> </Provider>,
  document.getElementById('root')
);

複製代碼

打完收工!你剛剛完成了從 Redux 到 React Context API 的遷移。若是你如今啓動你的應用,你會發現整個應用運行如常。惟一不一樣的是,你的應用再也不使用 Redux 了。

「新增的 React Context API 在減少應用體積方面是 Redux 的優良替代品。」

題外話:使用 Auth0 使你的 React 應用更安全

原文中有關於 Auth0 使用的詳細教程,但譯者認爲此處內容和本文主題關係不大,故不做翻譯。感興趣者可移步原文閱讀該部份內容。

總結

Redux 是一個高級狀態管理庫,適合在構建大規模 React 應用時使用。另外一方面,Context API 能夠用於字節大小級別數據更改的小規模 React 應用中。經過使用 Context API,你沒必要像 reducersactions 等同樣編寫大量代碼,就能經過狀態變化完成邏輯展示。

相關文章
相關標籤/搜索