Redux定义

Redux官网上是这么定义的:A Predictable State Container for JS Apps,直译过来就是一个使用在JS应用上的可预测的状态容器。

Redux解决的问题

React Native本身是基于前端的React框架,它是通过State来管理页面显示和参数更新。如果在交互比较复杂的页面、需要多页面状态传递或者同步更新状态的情况,状态管理就会比较麻烦。使用Redux就可以解决这些问题。

Redux的核心逻辑和概念

Redux的核心逻辑是集中定义和管理状态和动作执行,各个页面使用connect方法绑定相应的方法和状态,并通过发送动作指令更新页面显示。达到状态和操作与页面隔离的效果。

State

State即状态,是React框架最基础的概念之一,通过更改状态实时更新页面显示。

1
2
3
4
{
prop1: value1,
prop2: value2,
}

Action

Action是指指令或者动作。在Redux中,页面不直接管理状态,每个页面都是通过发送Action间接地更新状态。Action中有两个参数,一个是Type,字符串类型,代表Action的类型和唯一标识,一个是payload,代表传递的参数,可以是任意类型。

1
2
3
4
{
type: 'EXAMPLE_ACTION',
payload: 'args'
}

Reducer

Redux中状态和动作都是集中管理,管理者被称为ReducerReducer接收StateAction参数,根据传过来的Action类型和参数进行处理并更新State

1
2
3
4
5
6
7
8
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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时,直接抽取即可

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

Thunk

上面介绍的是普通Action,但如果是执行的动作需要异步执行后更新状态的就不适用了,因此Redux引入了中间件Thunk,在引入**@reduxjs/toolkit**后创建异步Action方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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,分别代表正在执行、成功执行和执行失败

1
2
3
4
5
6
7
8
9
10
11
12
13
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

StoreRedux的核心类,它的作用是管理所有的Reducer和中间件,并作为参数传递到项目的根视图组件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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**。

1
2
3
4
5
# NPM
npm install @reduxjs/toolkit

# Yarn
yarn add @reduxjs/toolkit

然后安装Redux核心库

1
2
3
4
5
# NPM
npm install redux

# Yarn
yarn add redux

创建Slice

创建Slice时会同步创建StateReducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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作为参数:

1
2
3
4
5
6
7
8
9
AppRegistry.registerComponent(appName, () => ProviderContainer);

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

下面是App.js的主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

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