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

相关文章