Redux使用教程

tecentID39C3D發表於2016-11-18
本教程假設你有React 和 ES6/2015經驗。首先從沒有使用Redux最簡單情況開始,演示到使用Redux從無到有的變化過程,從對比中體會Redux好處。

首先建立一個React元件components/ItemList.js用來抓取和顯示條目列表。設定這個靜態元件帶有狀態包含各種items輸出,2個boolean狀態表示在抓取載入時正常和出錯的結果。

import React, { Component } from 'react';
class ItemList extends Component {
constructor() {
super();
this.state = {
items: [
{
id: 1,
label: 'List item 1'
},
{
id: 2,
label: 'List item 2'
},
{
id: 3,
label: 'List item 3'
},
{
id: 4,
label: 'List item 4'
}
],
hasErrored: false,
isLoading: false
};
}
render() {
if (this.state.hasErrored) {
return <p>Sorry! There was an error loading the items</p>;
}
if (this.state.isLoading) {
return <p>Loading…</p>;
}
return (
<ul>
{this.state.items.map((item) => (
<li key={item.id}>
{item.label}
</li>
))}
</ul>
);
}
}
export default ItemList;
<p class="indent">


當渲染時,這個元件應該輸出4個條目列表,但是如果設定 isLoading或hasErrored為true時,也會有相應狀態輸出。

假設我們從 JSON API抓取items,使用Fetch API實現非同步請求,它比傳統的XMLHttpRequest更方便,能夠返回一個響應的promise,Fetch並不適合所有瀏覽器,需要加入你專案依賴:
npm install whatwg-fetch --save

抓取邏輯很簡單:
1.首先,設定初始化items為空陣列[]
2.加入抓取方法,設定loading和錯誤資訊

fetchData(url) {
this.setState({ isLoading: true });
fetch(url)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
this.setState({ isLoading: false });
return response;
})
.then((response) => response.json())
.then((items) => this.setState({ items })) // ES6 property value shorthand for { items: items }
.catch(() => this.setState({ hasErrored: true }));
}
<p class="indent">


這樣,當元件安裝時我們可以呼叫它:

componentDidMount() {
this.fetchData('http://5826ed963900d612000138bd.mockapi.io/items');
}
<p class="indent">

這樣,整個元件程式碼:

class ItemList extends Component {
constructor() {
this.state = {
items: [],
};
}
fetchData(url) {
this.setState({ isLoading: true });
fetch(url)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
this.setState({ isLoading: false });
return response;
})
.then((response) => response.json())
.then((items) => this.setState({ items }))
.catch(() => this.setState({ hasErrored: true }));
}
componentDidMount() {
this.fetchData('http://5826ed963900d612000138bd.mockapi.io/items');
}
render() {
}
}
<p class="indent">


現在,我們已經有一個沒有使用redux普通的抓取元件,從一個REST端點抓取條目列表,在條目全部抓取出現之前,顯示loading。。狀態,而如果發生錯誤,可以顯示錯誤。

無論如何,一個元件不應該包含抓取資料的邏輯,資料也不應該儲存在元件的狀態中,要解決這兩個問題,就需要redux。

下面我們將上面案例程式碼轉換為redux,我們需要加入Redux,為了在Redux中實現非同步呼叫,我們還需要Redux Thunk

什麼是Thunk?Thunk是一個函式,用於包裝一個表示式以延遲其計算結果的獲得,看下面程式碼:

// 立即計算 1 + 2 
// x === 3
let x = 1 + 2;

//  1 + 2計算被延遲
// foo 能在以後被呼叫時再進行計算
// foo 就是一個 thunk!
let foo = () => 1 + 2;
<p class="indent">


下面是在我們當前案例安裝Redux, React Redux和Redux Thunk:
npm install redux react-redux redux-thunk --save

理解Redux
Redux有一些核心原理我們需要理解:
1.有一個全域性狀態物件管理整個應用的狀態,在這個案例中,它是類似於我們這個最初元件的狀態,這屬於真相單一來源。

2.只有一個辦法才能修改狀態,只能透過一個動作action,這是一個用來表示改變操作的物件,Action Creators是專門用來派發每個改變的函式,所做的就是返回一個動作action物件。

3.當一個動作action被派發時,一個Reducer函式將實際改變這個動作所要改變的狀態,如果動作不適合這個Reducer,就返回已經存在狀態。

4.Reducer時純函式,它們不會有副作用,沒有可變狀態,它們都必須返回一個修改的複製,

5.獨立reducer被結合到單個rootReducer上,以建立狀態的各種離散狀態。

6.Store是將上面所有東西都結合一起,透過使用rootReducer代表狀態,允許你真正dispatch動作action

7.對於在React中使用Redux,<Provider />元件包裝整個應用,傳遞store下傳到所有子成員。

設計狀態
從開始工作到現在,我們已經明白我們狀態需要有三個屬性: items,hasErrored,isLoading,對應三個動作action。

但是Action Creators不同於動作action,不需要與狀態有1:1關係,我們需要第四個action creator來呼叫其他三個action creators,這取決於抓取資料的狀態,這第四個action creator幾乎等同於我們這個案例原始fetchData()方法,但是不是使用this.setState({ isLoading: true }) 方法來直接修改狀態,我們透過派發dispatch一個動作action來做同樣事情:dispatch(isLoading(true))

建立動作action
讓我們建立actions/items.js檔案來放我們的action creators,我們首先開始3個簡單動作:

export function itemsHasErrored(bool) {
    return {
        type: 'ITEMS_HAS_ERRORED',
        hasErrored: bool
    };
}
export function itemsIsLoading(bool) {
    return {
        type: 'ITEMS_IS_LOADING',
        isLoading: bool
    };
}
export function itemsFetchDataSuccess(items) {
    return {
        type: 'ITEMS_FETCH_DATA_SUCCESS',
        items
    };
}
<p class="indent">

正如之前提到,action creators會返回一個動作action,我們使用export以便以後在其它程式碼地方使用它們。

第二個action create獲取bool(true/false)作為引數,返回帶有意義的一個type和一個分配有具體相應屬性的bool這兩個型別的物件。

第三個itemsFetchSuccess是當資料被成功抓取時呼叫,將資料作為items傳遞,透過ES6的value shorthands魔術,我們返回一個物件,帶有屬性items,它的值將是items的陣列。

現在我們有3個動作action代表我們的狀態,我們將轉換原來元件的fetchData方法為itemsFetchData()這樣新的action creator。

預設情況下,Redux的action creators不會支援非同步動作,比如非同步抓取資料,這裡就需要使用Redux Thunk,它能讓你編寫action creator返回一個函式而不是一個動作action物件,內部函式能夠接受store方法dispatch和getState作為引數,但是我們只使用dispatch。

如果不使用thunk,實現非同步的最簡單方法是5秒後手工觸發itemsHasErrored方法:

export function errorAfterFiveSeconds() {
    // We return a function instead of an action object
    return (dispatch) => {
        setTimeout(() => {
            // This function is able to dispatch other action creators
            dispatch(itemsHasErrored(true));
        }, 5000);
    };
}
<p class="indent">

現在有了Thunk,我們編寫itemsFetchData方法:

export function itemsFetchData(url) {
    return (dispatch) => {
        dispatch(itemsIsLoading(true));
        fetch(url)
            .then((response) => {
                if (!response.ok) {
                    throw Error(response.statusText);
                }
                dispatch(itemsIsLoading(false));
                return response;
            })
            .then((response) => response.json())
            .then((items) => dispatch(itemsFetchDataSuccess(items)))
            .catch(() => dispatch(itemsHasErrored(true)));
    };
}

<p class="indent">


待續。。。

A Dummy’s Guide to Redux and Thunk in React – Medi

[該貼被tecentID39C3D於2016-11-18 13:16修改過]

相關文章