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中状态和动作都是集中管理,管理者被称为Reducer,Reducer接收State和Action参数,根据传过来的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使用中,我们需要自己创建Action和Reducer,并用Switch或者if else语句区分不同的Action,步骤非常繁琐。现在Redux官方推荐使用更方便更现代化的工具**@reduxjs/toolkit,这个工具集成了createAction、createReducer等方法,非常方便。不过这两个方法一般也不用,在tookit提供了一个新的类Slice,创建Slice时也会同时创建State、Reducer和Action。
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; }, }, });
|
在需要使用Reducer和Action时,直接抽取即可
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是否可以继续执行,如果返回false,Action会终止执行而且没有回调。如果希望返回false后有rejected回调,可以设置dispatchConditionRejection为true。
异步Action执行完成后,回调是在Slice的extraReducers中,异步Action有三个状态:pending、fulfilled和rejected,分别代表正在执行、成功执行和执行失败
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
Store是Redux的核心类,它的作用是管理所有的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时会同步创建State、Reducer
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;
|
在这里创建了名为count的Slice,计算器初始值为0,并在Reducer中定义了两个Action:incrementAction和decrementAction,根据传过来的参数确定每次加减的数值。后面两行export代码确保外部能够访问这里创建的Action和reducer。
创建Store
接下来就是创建Store,创建时会传入刚刚创建的reducer。
注意:在页面获取状态值的时候中间一定要先获取reducer,然后再获取reducer里的状态值,例如获取countReducer里的value:state.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用于输入每次增加或者减小的数值,下面有一个加号按钮和一个减号按钮,中间是显示当前数值的文本。
mapStateToProps和mapDispatchToProps,的作用是映射Slice中定义的State和Action到当前页面,在使用时直接this.props.value调用即可。最后通过Redux的connect方法将这些映射和当前页的组件连接起来。
本文的Demo可以在这里查看。
以上就是Redux的入门教程,想深入了解Redux的使用可以参考Redux官方文档,想进一步了解Redux Toolkit可以参考Redux Tookit官方文档。