开头介绍
虽然网上到处都是react-native的教程, 也到处都是redux的教程, immutable的介绍也是十分的多, 但是把以上所有结合的教程是少之又少, 寥寥几篇都是英文文档, 对于新手来说是相当不友好. 而且大部分介绍都是理论性知识, 并没有直接一个demo的展示, 当初相当困扰, 现在跑通了问题就把积累的经验写一下教程. 如果能帮到人就是很好的.
redux是react的状态管理库, 如果光使用react的话我觉得使用mobx比redux更简单方便, 只是因为app性能消耗大, 需要用不可变数据(immutable)的处理, 而mobx是可变数据的库, 两者并不结合,所以采用redux. navigation方面react-navigation就十分方便. 官方推荐的是react-navigation和react-native-navigation, 两者的使用方式类似, 以下先把使用的库的文档的地址发出来.
react-native: https://facebook.github.io/react-native/
redux: https://redux.js.org/
immutable: https://facebook.github.io/immutable-js/
react-navigation: https://reactnavigation.org/
创建项目
本文章使用操作系统macOS High Sierra 10.13.4.
采用create-react-native-app进行创建项目, 与react-native-cli对比, create-react-native-app集成了expo所以更加方便快捷, 但是问题在于打包的时候必须要借助expo或者xcode/Android studio进行打包. 而使用react-native-cli则可以通过命令行进行打包. 实际项目使用个人推荐react-native-cli, 但平时的练习可以使用create-react-native-app. 因为使用了yarn, 以下都是yarn的环境.
yarn global add create-react-native-app
create-react-native-app demoProject
cd demoProject
yarn start
路由配置
执行后根据终端提示点击i打开模拟器, 然后就能图片下的这样子,证明代码已经成功运行. 现在开始安装react-navigation作为路由管理.
yarn add react-navigation
可以看到现在项目的代码现在都在App.js上面, 我们开始新建文件夹进行分类. 首先新建src文件夹, 里面新建navigations文件夹, 里面新建Index.js文件.
1 | // Index.js |
修改App.js, 将组建指向路由组建1
2
3
4
5
6
7
8
9
10
11// App.js
import React from 'react';
import AppNavigation from "./src/navigations/Index";
export default class App extends React.Component {
render() {
return (
<AppNavigation />
);
}
}
新建src/pages文件夹, 新建Home和Mine页面, 作为navigation的指向页1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// src/pages/Home.js
import React from 'react';
import { View, Text } from 'react-native';
class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>HomeScreen!</Text>
</View>
);
}
}
export default HomeScreen;
1 | // src/pages/Mine.js |
此时目录结构如下
同时看到模拟器已经转为下图
到此路由配置完成.
状态管理
接着开始redux的配置. 这里写一个简单的数字增加减少demo进行演示状态的分发. 首先需要安装redux, react-redux, 还有redux-thunk
yarn add redux
yarn add react-redux
yarn add redux-thunk
因为redux需要在全局注册, 这里需要修改App.js文件, 增加全局控制的store1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// App.js
import React from 'react';
import { Provider } from 'react-redux';
import configureStore from './src/store/Index';
import AppNavigation from "./src/navigations/Index";
const store = configureStore();
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<AppNavigation />
</Provider>
);
}
}
在src/store增加Index.js文件, 作为控制store状态集中管理1
2
3
4
5
6
7
8
9
10// src/store/Index.js
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers/Index';
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
export default function configureStore(initialState) {
return createStoreWithMiddleware(rootReducer, initialState);
}
新增src/reducers/Index.js作为reduces的管理, reducers指定了应用状态的变化如何响应action并分发到store. 用rootReducer包含所有的reducers可以方便管理不同页面的状态.1
2
3
4
5
6
7
8
9// src/reducers/Index.js
import { combineReducers } from 'redux';
import homeState from './Home';
const rootReducer = combineReducers({
homeState,
});
export default rootReducer;
1 | // src/reducers/Home.js |
至此, store的状态的就配置完成, 这时候在页面就可以获取对应的store值,我们回到src/pages/Home.js进行配置, 拿到存在reducers里面的number1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// src/pages/Home.js
import React from 'react';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>HomeScreen!</Text>
<Text>{ this.props.number }</Text>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
number: state.homeState.number,
}
}
export default connect(mapStateToProps)(HomeScreen);
出现的页面应该应可以显示我们在reducers里面的number为1, 目录结构如下.
接着就是通过action的分发实现数字的增减. 在src/pages/Home.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// src/pages/Home.js
import React from 'react';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import {
addNumber,
reduceNumber
} from '../actions/Actions';
class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>HomeScreen!</Text>
<Text>{ this.props.number }</Text>
<Text onPress={ this.props.onAdd }>ADD</Text>
<Text onPress={ this.props.onReduce }>REDUCE</Text>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
number: state.homeState.number,
}
}
const mapDispatchToProps = (dispatch) => {
return {
onAdd: () => dispatch(addNumber()),
onReduce: () => dispatch(reduceNumber()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
这里通过mapDispatchToProps把组件状态进行传递过去. 在src新建actions文件夹, 里面新增Actions.js和Types.js1
2
3
4
5
6
7
8
9
10
11
12
13
14// src/actions/Actions.js
import * as types from './Types';
export const addNumber = () => {
return {
type: types.ADD_NUMBER
}
}
export const reduceNumber = () => {
return {
type: types.REDUCE_NUMBER
}
}
1 | // src/actions/Types.js |
然后在src/reducers/Home.js进行引入types, 修改刚才已经定义的number的值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// src/reducers/Home.js
import {
ADD_NUMBER,
REDUCE_NUMBER
} from '../actions/Types';
const initState = {
number: 1,
};
const mainReducer = (state = initState, action) => {
switch (action.type) {
case ADD_NUMBER:
return {
...state,
number: state.number + 1
}
case REDUCE_NUMBER:
return {
...state,
number: state.number - 1
}
default:
return state
}
};
export default mainReducer;
在模拟器上进行点击, 发现已经可以通过ADD和REDUCE进行把number的值进行增减.到此redux已经配置完成. 目录结构此时如下
接下来就是关于immutable的配置, 关于immutable的介绍网上已经很多, 就不需要再过多的介绍,这里直接开始讲述如何进行配置.
immutable配置
安装依赖库
yarn add immutable
yarn add redux-immutable
immutable是将数据改为不可变数据, 因此需要操作的主要在reducers一块.这里需要先把src/reducers/Index的combineReducers改为使用redux-immutable的combineReducers1
2
3
4
5
6
7
8
9// src/reducers/Index.js
import { combineReducers } from 'redux-immutable';
import homeState from './Home';
const rootReducer = combineReducers({
homeState,
});
export default rootReducer;
然后到src/reducers/Home.js修改state改为immutable使用的数据格式, 同时action修改state的方式也要改为immutable支持的格式, 即将js数据类型转换immutable数据类型.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// src/reducers/Home.js
import { Map } from 'immutable';
import {
ADD_NUMBER,
REDUCE_NUMBER
} from '../actions/Types';
const initState = Map({
number: 1,
});
const mainReducer = (state = initState, action) => {
switch (action.type) {
case ADD_NUMBER:
return state.set('number', state.get('number') + 1)
case REDUCE_NUMBER:
return state.set('number', state.get('number') - 1)
default:
return state
}
};
export default mainReducer;
这时候关于redux转换的已经完成了, 还差最后一步就是在组件里面修改获取immutable的方式.这里采用官方getIn的方式,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// src/pages/Home.js
import React from 'react';
import { View, Text } from 'react-native';
import { connect } from 'react-redux';
import {
addNumber,
reduceNumber
} from '../actions/Actions';
class HomeScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>HomeScreen!</Text>
<Text>{ this.props.number }</Text>
<Text onPress={ this.props.onAdd }>ADD</Text>
<Text onPress={ this.props.onReduce }>REDUCE</Text>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
number: state.getIn(['homeState', 'number']),
}
}
const mapDispatchToProps = (dispatch) => {
return {
onAdd: () => dispatch(addNumber()),
onReduce: () => dispatch(reduceNumber()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
至此immutable的配置也已经完成, 关于更多redux, immutable的使用的方法请查看官网文档, 这里不作更多解释. 本项目例子github已放github仓库 https://github.com/Lanceloft/react-native-demo 欢迎star