【React Native教程】Redux入門教程

語言: CN / TW / HK

Redux在 官網 上是這麼定義的: A Predictable State Container for JS Apps ,直譯過來就是一個使用在JS應用上的可預測的狀態容器。

Redux解決的問題

React Native 本身是基於前端的 React 框架,它是通過 State 來管理頁面顯示和引數更新。如果在互動比較複雜的頁面、需要多頁面狀態傳遞或者同步更新狀態的情況,狀態管理就會比較麻煩。使用 Redux 就可以解決這些問題。

Redux的核心邏輯和概念

Redux的核心邏輯是集中定義和管理狀態和動作執行,各個頁面使用 connect 方法繫結相應的方法和狀態,並通過傳送動作指令更新頁面顯示。達到狀態和操作與頁面隔離的效果。

State

State即狀態,是 React 框架最基礎的概念之一,通過更改狀態實時更新頁面顯示。

{
	prop1: value1,
	prop2: value2,
}

Action

Action是指指令或者動作。在 Redux 中,頁面不直接管理狀態,每個頁面都是通過傳送 Action 間接地更新狀態。 Action 中有兩個引數,一個是 Type ,字串型別,代表 Action 的型別和唯一標識,一個是 payload ,代表傳遞的引數,可以是任意型別。

{
	type: 'EXAMPLE_ACTION',
	payload: 'args'
}

Reducer

Redux 中狀態和動作都是集中管理,管理者被稱為 ReducerReducer 接收 StateAction 引數,根據傳過來的 Action 型別和引數進行處理並更新 State

function rootReducer(state = initialState, action) {
  if (action.type === 'FIRST_ACTION') {
    return Object.assign({}, state, {
      props: newValue
    });
  }
  return state;
}

Slice

在以往的 Redux 使用中,我們需要自己建立 ActionReducer ,並用 Switch 或者 if else 語句區分不同的 Action ,步驟非常繁瑣。現在 Redux 官方推薦使用更方便更現代化的工具 @reduxjs/toolkit ,這個工具集成了 createActioncreateReducer 等方法,非常方便。不過這兩個方法一般也不用,在 tookit 提供了一個新的類 Slice ,建立**Slice時也會同時建立 StateReducerAction

const mySlice = createSlice({
	name: "sliceName",
	initialState: {
	    prop1: "",
	    prop2: false,
	},
	reducers: {
	    action1: (state, action) => {
	        console.debug('action1 done'+action.type);
	    },
	    action2: (state) => {
	        state.prop1 = "123";
	        state.prop2 = true;
	    },
	},
});

在需要使用 ReducerAction 時,直接抽取即可

const { action1, action2 } = authSlice.actions;
const myReducer = mySlice.reducer;

Thunk

上面介紹的是普通 Action ,但如果是執行的動作需要非同步執行後更新狀態的就不適用了,因此 Redux 引入了中介軟體 Thunk ,在引入 @reduxjs/toolkit 後建立非同步 Action 方法如下:

export const doLogin = createAsyncThunk(
    'user/login',
    async ({username, password, code}) => {
        return await API.fetchData({
            path: 'doLogin',
            params: {
                code: code,
                password: AESTool.encrypt(password),
                userName: username,
            }
        });
    },
    {
        condition: ({username, password, code}) => {
            if (checkStringEmpty(username)) {
                HUD.show("請輸入使用者名稱!");
                return false;
            } else if (checkStringEmpty(password)) {
                HUD.show("請輸入密碼!");
                return false;
            } else if (checkStringEmpty(code)) {
                HUD.show("請輸入驗證碼!");
                return false;
            }
            return true;
        },
        dispatchConditionRejection: true
    }
)

在上面的程式碼中,在 condition 可以控制這個非同步 Action 是否可以繼續執行,如果返回 falseAction 會終止執行而且沒有回撥。如果希望返回 false 後有 rejected 回撥,可以設定 dispatchConditionRejectiontrue

非同步 Action 執行完成後,回撥是在 SliceextraReducers 中,非同步 Action 有三個狀態: pendingfulfilledrejected ,分別代表正在執行、成功執行和執行失敗

extraReducers: {
    [ doLogin.pending ]: () => {
        Loading.show();
    },
    [ doLogin.fulfilled ]: (state, action) => {
        Loading.hidden();
        console.debug(action.payload)
    },
    [ doLogin.rejected ]: (state, action) => {
        Loading.hidden();
        console.warn(action.error);
    },
}

Store

Store是 Redux 的核心類,它的作用是管理所有的 Reducer 和中介軟體,並作為引數傳遞到專案的根檢視元件中。

const middleware = [
    ...getDefaultMiddleware(),
    CustomMiddleWare,
];

export const store = configureStore({
    reducer: {
        auth: authReducer,
        common: commonReducer
    },
    middleware,
});

<Provider store={store}>
    <StatusBar barStyle={'light-content'}/>
    <App/>
</Provider>

Redux在React Native中的使用

下面以一個簡單的計數器為例講解一下如果在 React Native 中使用 Redux

安裝依賴

首先需要安裝 @reduxjs/toolkit ,可以使用 NPM 或者 Yarn

# NPM
npm install @reduxjs/toolkit

# Yarn
yarn add @reduxjs/toolkit

然後安裝 Redux 核心庫

# NPM
npm install redux

# Yarn
yarn add redux

建立Slice

建立 Slice 時會同步建立 StateReducer

import {createSlice} from '@reduxjs/toolkit';

const countSlice = createSlice({
    name: "count",
    initialState: {
        value: 0
    },
    reducers: {
        incrementAction: (state, action) => {
            state.value += action.payload;
        },
        decrementAction: (state, action) => {
            state.value -= action.payload;
        },
    },
});

export const {incrementAction, decrementAction } = countSlice.actions;

export const countReducer = countSlice.reducer;

在這裡建立了名為 countSlice ,計算器初始值為0,並在 Reducer 中定義了兩個 ActionincrementActiondecrementAction ,根據傳過來的引數確定每次加減的數值。後面兩行 export 程式碼確保外部能夠訪問這裡建立的 Actionreducer

建立Store

接下來就是建立 Store ,建立時會傳入剛剛建立的 reducer

注意:在頁面獲取狀態值的時候中間一定要先獲取 reducer ,然後再獲取 reducer 裡的狀態值,例如獲取 countReducer 裡的 valuestate.count.value

import {configureStore, createSlice, getDefaultMiddleware} from "@reduxjs/toolkit";
import {countReducer} from './slices/CountSlice'

const middleware = [
    ...getDefaultMiddleware(),
];

export const store = configureStore({
    reducer: {
        count: countReducer, 
    },
    middleware,
});

至此, Redux 部分就準備好了,接下來就是頁面的互動部分了。

頁面嵌入Redux

index.js 檔案中將 Provider 更改為 App 的根控制元件,並傳入 store 作為引數:

AppRegistry.registerComponent(appName, () => ProviderContainer);

const ProviderContainer = () => {
    return (
        <Provider store={store}>
            <App/>
        </Provider>
    );
}

下面是 App.js 的主要程式碼

class App extends Component {

  constructor(props) {
    super(props);
    this.state = {
      num: 1
    }
  }

  render() {
    return (
        <>
          <StatusBar barStyle="dark-content" />
          <SafeAreaView>
            <View style={styles.container}>
              <TextInput style={styles.textInput} onChangeText={(text)=>{
                this.setState({
                  num: parseInt(text)
                })
              }}>{isNaN(this.state.num) ? 1 : this.state.num}</TextInput>
              <View style={styles.buttonContainer}>
                <TouchableOpacity style={styles.button} activeOpacity = {.9} onPress={() => {
                  this.props.decrement(this.state.num)
                }}>
                  <Text style={styles.buttonText}>-</Text>
                </TouchableOpacity>
                <Text style={styles.text}>{this.props.value}</Text>
                <TouchableOpacity style={styles.button} activeOpacity = {.9} onPress={() => {
                  this.props.increment(this.state.num)
                }}>
                  <Text style={styles.buttonText}>+</Text>
                </TouchableOpacity>
              </View>
            </View>
          </SafeAreaView>
        </>
    );
  }

}

const mapStateToProps = (state) => {
  return {
    value: state.count.value,
  }
}

function mapDispatchToProps(dispatch) {
  return {
    increment: (num) => dispatch(incrementAction(num)),
    decrement: (num) => dispatch(decrementAction(num)),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(App);

上面的 TextInput 用於輸入每次增加或者減小的數值,下面有一個加號按鈕和一個減號按鈕,中間是顯示當前數值的文字。

mapStateToPropsmapDispatchToProps ,的作用是對映 Slice 中定義的 StateAction 到當前頁面,在使用時直接 this.props.value 呼叫即可。最後通過 Reduxconnect 方法將這些對映和當前頁的元件連線起來。

本文的 Demo 可以在 這裡 檢視。

以上就是 Redux 的入門教程,想深入瞭解 Redux 的使用可以參考 Redux官方文件 ,想進一步瞭解 Redux Toolkit 可以參考 Redux Tookit官方文件