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官方文檔

相關文章