1. 程式人生 > 實用技巧 >React Redux

React Redux

寫在前面

在前面介紹了 Redux,並用原生的 Redux 配合 React 開發了一個 todo-list 小 demo。

但是後來 React 的作者為了讓 React 使用者更好地使用 Redux 進行 React 的專案開發,在 Redux 的基礎上封裝了一個專用庫:React-Redux。React-Redux 在 Redux 的基礎上根據 React 的特性進行了優化,因此現在大部分的 React 的專案開發的狀態管理工具都是 React-Redux,但使用 React-Redux 的前提還是要學會 Redux,因為 React-Redux 是在 Redux 的基礎上提供了額外的一個方法和一個元件。

React-Redux 的厲害之處不是說額外提供了一個方法和一個元件,而是其提出了一種新的開發思路。在僅僅使用 Redux 的過程中,我們的元件在獲得 state 後要在元件內部程式碼中篩選出該元件需要的資料,在元件呼叫事件函式時 dispatch 改變狀態的動作。而 React-Reudx 提供了一種新的思路,將元件劃分為 UI 元件容器元件

UI 元件僅僅負責介面的展示,不負責任何的資料處理操作,因此不能在 UI 元件裡使用 Redux 的 API ,如 getState、dispatch 等等。那麼 UI 元件應該如何使用動態的資料狀態呢?答案是使用元件的 props 引數,UI 元件用到的所有資料都在 Props 中接收到。

與 UI 元件相反,容器元件負責資料狀態的獲得和操作,處理業務邏輯,如 getState、dispatch 等,不負責 UI 的呈現。

將處理資料的容器元件包裹住只負責頁面呈現的 UI 元件,二者包裹在一起就形成了一個完整的帶有資料業務邏輯的元件,這樣一來,狀態處理 和 UI 呈現分離,更容易管理狀態和 UI 樣式,程式碼更加結構化。

那麼 React-Redux 如何使用呢?

1. connect 方法

React-Redux 提供了 connect 方法用於將 UI 元件和 容器元件 組合起來。connect 是一個返回一個函式的方法。該方法接收兩個引數:mapStateToPropsmapDispatchToProps

,返回的函式接收一個 UI 元件,並返回一個已經包裹了 UI 元件的容器元件。包裹了 UI 元件的容器元件就是一個包含狀態的完整的元件。

因此其實將 UI 元件和 容器元件 分開來說不太合適,將 UI 元件通過 connect 方法包裹一層狀態業務邏輯就是容器元件了,因此,在頁面中載入元件時,不再是使用 UI 元件了,而是使用包裹了狀態業務邏輯的 容器元件。

那麼這個狀態的業務邏輯是如何告訴負責包裹 UI 元件的 connect 方法的呢? 就是通過給 connect 方法傳遞引數 mapStateToProps 和 mapDispatchToProps 實現的。

const container =  connect(
    mapStateToProps,
    mapDispatchToProps
)(VisibleTodoList)

1.1 mapStateToProps

mapStateToProps 字面意思就是對映狀態到屬性,就是說其負責將 Redux 中獲得的狀態 state 通過處理得到該元件需要的資料狀態,然後對映到 UI 元件的 Props 屬性上。

這種對映方式就是通過物件的鍵值對實現的,但是如果 mapStateToProps 直接是一個物件,那在該物件中對 Redux 狀態的獲取還得通過 getState() 手動獲取,既然肯定是要獲取當前狀態,不如 Reac-Redux 幫我們做好算了。因此 React-Redux 就將 mapStateToProps 規定為是一個返回物件的函式,該函式接收兩個引數 stateownProps,其中 state 就是當前的狀態,React-Redux 會在 Redux 的狀態變化時自動呼叫 mapStateToPrps 的,並將當前的狀態作為第一個引數傳遞。

那麼第二個引數 ownProps 是什麼呢?聽字母的意思就是自己的屬性的意思,容器元件在頁面中載入時,頁面也可以通過屬性的方式傳遞資料給該容器元件,就像之前元件接收屬性作為 Props 一樣,ownProps就是容器元件在使用時,通過元件傳遞屬性的方式接收的自己的資料狀態。

因此我們知道,mapStateToProps 訂閱了 Redux 的 store,當狀態發生變化時會自動執行,重新計算資料,從而重新渲染頁面。但是還有一種方式會使該函式重新執行,就是其依賴的 ownProps 發生變化時也會重新計算並渲染。

通過在 connect 方法中傳遞 mapStateToProps,connect 方法就會將 mapStateToprops 函式返回的物件中的全部屬性作為 connect 連線的 UI 元件的 Props 物件的屬性傳遞給 UI 元件的。因此在 UI 元件中的 Props 可以直接拿到 mapStateToState 返回物件裡的屬性。

要點總結:

1. mapStateToProps 用於外部向 UI 元件傳遞資料狀態

2. mapStateToProps 是一個函式,接收 state 和 ownProps 兩個引數

3. mapStateToProps 返回一個物件,物件的全部屬性可在其連線的 UI 元件的 Props 物件中拿到

4. mapStateToProps 訂閱了 store 和 ownProps

const mapStateToProps = (state, ownProps)=>{
    return {
        todoList: getVisibleTodoList(state.todoList, state.filter)
    }
}

1.2 mapDispatchToProps

mapStateToProps 是負責外部傳遞資料到 UI 元件內使用的,那如果 UI 元件想要改變外部狀態的時候該怎麼辦呢?

在 Redux 中就是通過在事件函式中使用 store.dispatch(action) 方法觸發改變狀態的動作。React-Redux 規定了,在 UI 元件中不要包含業務邏輯的處理,不要在 UI 元件中使用 Redux API。

ok,那就將該事件處理函式定義在容器元件的業務邏輯層中,這負責 UI 元件向外傳遞資料操作的就是 mapDispatchToprops。

mapDispatchToProps 字面意思就是對映派遣到屬性。就是將 UI 元件中的事件處理函式對映到 UI 元件的 Props 物件中,UI 元件在註冊事件回撥函式時直接使用 Props 中傳遞來的事件處理函式值就可以。

一般在 UI 元件的事件處理函式中都會涉及到對 Redux 中 state 的改變,因此會經常用到 store.dispatch,但是使用 dispatch 方法做的只有一件事就是接收一個 action 然後改變狀態。因此 React-Redux 規定,mapDispatchToProps 可以是一個物件,也可以是一個函式。

當 mapDispatchToProps 是一個物件時,鍵值對中的值必須是一個 Action Creator 函式。會由 React-Redux 自動 dispatch。 如下:

const mapDispatchToProps = {
  onClick: (filter) => {
    type: 'SET_VISIBILITY_FILTER',
    filter: filter
  };
}

當 mapDispatchToProps 是一個函式時,需要返回一物件,物件的鍵值對中的值必須是一個手動 dispatch 的函式。如下:

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    onClick: () => {
      dispatch({
        type: 'SET_VISIBILITY_FILTER',
        filter: ownProps.filter
      });
    }
  };
}

要點總結:

1. mapDispatchToProps 負責 UI 元件向外部傳遞資料操作。

2. mapDispatchToProps 可以是一個物件,也可以是一個函式。

3. mapDispatchToProps 物件或返回物件的屬性可以在 UI 元件的 Props 中拿到

2. <Provide> 元件

使用 connect 方法生成的 容器元件 的正常使用依賴於 Redux 的倉庫例項:store。因為有了 store ,容器元件才能在 mapStateToProps 中拿到 state 當前狀態,在 mapDispatchToProps 中拿到 dispatch 方法。

React-Redux 提供了 <Provide> 元件,用 <Provide> 元件包裹住一個元件,然後在 <Provide> 元件中傳入 store 值,那麼在 <Provide> 包裹的元件及其其所有後代元件都可以直接使用到 store,從而也都能順利拿到 state 和 dispatch 方法了。<Provide> 就是用於傳遞區域性的全域性變數,類似上下文 context

const store = createStore(reducer, applyMiddleware(thunk))
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

原始碼連結

在瞭解了 React-Redux 的使用方法後,再重新做一遍 todo-list 專案,就會使得程式碼組織結構更加清晰了,程式碼結構如下:

原始碼

參考連結

React-Redux 的用法