1. 前言:
react的状态管理首选Redux,Redux是javascript状态容器,提供可预测话的状态管理,可以让你构建异质化的应用,运行于不同的环境(客户端、服务器、原声应用),并且易于测试。React-redux的流程简图如下:
2.开始
1. 安装模块
npm install --save redux
npm install --save react-redux
npm install --save-dev redux-devtools
npm install --save react-navigation-redux-helpers
npm install --save redux-thunk
2. 概念解释:
<Provider>
组件:这个组件需要包裹在整个组件树的最外层。这个组件让根组件的所有子孙组件能够轻松的使用connect()方法绑定store。connect()
:这是react-redux提供的一个方法。如果一个组件想要相应状态的变化,就把自己作为参数传给connect()的结果,connect()方法会处理与store绑定的细节,并通过selector确定该绑定store中的那一部分的数据。selector
:这是你自己编写的一个函数。这个函数声明了你的组件需要整个store中的那一部分数据作为自己的props。dispatch
:每当你想要改变应用中的状态时,你就要dispatch一个action,这也是唯一改变状态的方法。
3. 步骤
一. 机械代码。这里是基本没什么变化的代码,每个项目基本都需要这么写。
js/App.js
App的根文件,这里需要注意的是<Provider>
包裹了根节点,只有这样才能将state传递给根节点下的所有子节点。
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import AppNavigator from './navigator/AppNavigator';
import store from './store'
import { SafeAreaView } from 'react-native';
type Props = {};
export default class App extends Component<Props> {
render() {
/**
* 将store传递给App框架
*/
return <Provider store={store}>
<SafeAreaView style={{flex:1}}>
<AppNavigator />
</SafeAreaView>
</Provider>
}
}
navigator/AppNavigator.js
导航根节点配置。
import { createStackNavigator, createSwitchNavigator, createAppContainer } from "react-navigation";
import WelcomePage from "../page/WelcomePage";
import DetailPage from "../page/DetailPage";
import HomePage from "../page/HomePage";
import { connect } from "react-redux";
import { View } from "react-native";
import { createReactNavigationReduxMiddleware, createReduxContainer } from 'react-navigation-redux-helpers';
import NavigationUtils from "./NavigationUtils";
import React, { Component } from 'react';
export const rootCom = "Main";//设置根路由
const InitNavigator = createStackNavigator({
WelcomePage: {
screen: WelcomePage,
navigationOptions: {
header: null
}
}
});
const MainNavigator = createStackNavigator({
HomePage: {
screen: props => {
NavigationUtils.MainNavigation = props.navigation;
return <HomePage {...props} />
},
navigationOptions: {
title: "HomePage",
header: ({navigation})=>{
NavigationUtils.rootNavigation = navigation;
return null;
}
}
},
DetailPage: {
screen: DetailPage,
navigationOptions: {
title: "DetailPage",
// header: null
}
}
});
export const RootNavigator = createAppContainer(createSwitchNavigator({
Init: InitNavigator,
Main: MainNavigator
}, {
navigationOptions: {
header: ({navigation})=>{
NavigationUtils.rootNavigation = navigation;
return <View/>;
}
}
}
));
/**
* 1.初始化react-navigation与redux的中间件,
* 该方法的一个很大的作用就是为reduxifyNavigator的key设置actionSubscribers(行为订阅者)
* 设置订阅者@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L29
* 检测订阅者是否存在@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L97
* 此处就是个固定的代码,使用react-navigation-redux-helpers就必须这么写
* @type {Middleware}
*/
export const middleware = createReactNavigationReduxMiddleware(
state => state.nav,
'root'
);
/**
* 2.将根导航器组件传递给 reduxifyNavigator 函数,
* 并返回一个将navigation state 和 dispatch 函数作为 props的新组件;
* 注意:要在createReactNavigationReduxMiddleware之后执行
*/
const AppWithNavigationState = createReduxContainer(RootNavigator, 'root');
/**
* State到Props的映射关系
* @param state
*/
const mapStateToProps = state => ({
state: state.nav//v2
});
/**
* 3.连接 React 组件与 Redux store
*/
export default connect(mapStateToProps)(AppWithNavigationState);
核心代码如下,这里基本就是些死代码,在导航根节点文件处照着写就行了:
//......other code omit......
import { createReactNavigationReduxMiddleware, createReduxContainer } from 'react-navigation-redux-helpers';
//......other code omit......
export const RootNavigator = createAppContainer(createSwitchNavigator({
Init: InitNavigator,
Main: MainNavigator
}, {
navigationOptions: {
header: ({navigation})=>{
NavigationUtils.rootNavigation = navigation;
return <View/>;
}
}
}
));
/**
* 1.初始化react-navigation与redux的中间件,
* 该方法的一个很大的作用就是为createReduxContainer的key设置actionSubscribers(行为订阅者)
* 设置订阅者@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L29
* 检测订阅者是否存在@https://github.com/react-navigation/react-navigation-redux-helpers/blob/master/src/middleware.js#L97
* 此处就是个固定的代码,使用react-navigation-redux-helpers就必须这么写
* @type {Middleware}
*/
export const middleware = createReactNavigationReduxMiddleware(
state => state.nav,
'root'
);
/**
* 2.将根导航器组件传递给 createReduxContainer 函数,
* 并返回一个将navigation state 和 dispatch 函数作为 props的新组件;
* 注意:要在createReactNavigationReduxMiddleware之后执行
*/
const AppWithNavigationState = createReduxContainer(RootNavigator, 'root');
/**
* State到Props的映射关系
* @param state
*/
const mapStateToProps = state => ({
state: state.nav//v3
});
//......other code omit......
/**
* 3.连接 React 组件与 Redux store
*/
export default connect(mapStateToProps)(AppWithNavigationState);
二. 业务代码
,这里是根据业务不同有变化的代码:
1. view层,触发action的位置:
import React,{Component} from "react";
import {View ,Text,StyleSheet,Button} from "react-native";
import { connect } from "react-redux";
import actions from "../action/index";
class Favorite extends Component{
render(){
return (
<View style={styles.container}>
<Text>收藏</Text>
<Button
title="改变主题色"
onPress={() => {
this.props.onThemeChange("#206");
}}
/>
</View>
);
}
}
//state变化后这里将被调用,用于筛选state中用户需要的数据注入props中(这个页面并不需要任何状态数据,所以留空)
const mapStateToProps = state => ({
});
//这里是将action模块中的function注入props中,注意一下上面onPress中this.props.onThemeChange("#206");这段代码
const mapDispatchToProps = dispatch =>({
onThemeChange:theme=>dispatch(actions.onThemeChange(theme))
});
//这里是一个处理state的高阶组件,你可以简单的理解为是将state、view、action连接在一起的胶水
export default connect(mapStateToProps,mapDispatchToProps)(Favorite);
const styles = StyleSheet.create({
container:{
flex:1,
backgroundColor:"#fff",
justifyContent:"center",
alignItems:"center"
}
});
2. action层
,这里主要写一些行为方法,比如发送请求,改变某些状态等等:
action模块的目录结构:
- action
- types.js 用于放置action的type值,type值在reducer中被识别,并以此作为标志位,判断进入某个reducer中处理数据
- index.js 用于compose(组合)所有子action文件中需要暴露出来的action方法。下面的theme就是子action文件的例子
- theme 这是个子action的例子
- index.js theme的action文件
action/types.js
代码
export default {
THEME_CHANGE:"THEME_CHANGE",
THEME_INIT:"THEME_INIT",
}
action/index.js
代码
import { onThemeChange } from "./theme";
export default {
onThemeChange
}
action/theme/index.js
import Types from "../types";
export function onThemeChange(theme) {
return { type: Types.THEME_CHANGE, theme: theme };
}
可以看出action是redux单向流程的第二步,用于修改store的状态和数据。
3. reducer
,此处用于对state进行处理,newState将被传递给view层并触发每个页面的mapDispatchToProps(这个流程是可以优化的)。
reducer目录结构
- render
- index.js 用于组合各个子reducer,这个例子里面的子reducer就是theme
- theme 子reducer例子
- index.js
render/index.js
import {combineReducers} from 'redux'
import theme from './theme'
import {rootCom, RootNavigator} from '../navigator/AppNavigator';
//1.指定默认state
const navState = RootNavigator.router.getStateForAction(RootNavigator.router.getActionForPathAndParams(rootCom));
/**
* 2.创建自己的 navigation reducer,
*/
const navReducer = (state = navState, action) => {
const nextState = RootNavigator.router.getStateForAction(action, state);
// 如果`nextState`为null或未定义,只需返回原始`state`
return nextState || state;
};
/**
* 3.合并reducer
* @type {Reducer<any> | Reducer<any, AnyAction>}
*/
const index = combineReducers({
nav: navReducer,
theme: theme,
});
export default index;
reducer/theme/index.js
import Types from '../../action/types';
const defaultState = {
theme:"blue",
};
export default function onAction(state = defaultState, action) {
switch (action.type) {
case Types.THEME_CHANGE:
return {
...state,
theme: action.theme,
};
default:
return state;
}
}
4. store
此处虽说是store模块但是其实是使用renducer
和middleware(中间件)
生成store的地方
store/index.js
import {applyMiddleware, createStore} from 'redux'
import thunk from 'redux-thunk'//用于支持异步action
import reducers from '../reducer'
import {middleware} from '../navigator/AppNavigator'
/**
* 自定义log中间件
* https://cn.redux.js.org/docs/advanced/Middleware.html
* @param store
*/
const logger = store => next => action => {
if (typeof action === 'function') {
console.log('dispatching a function');
} else {
console.log('dispatching ', action);
}
const result = next(action);
console.log('nextState ', store.getState());
return result;
};
const middlewares = [
middleware,
logger,
thunk,
];
/**
* 创建store
*/
export default createStore(reducers, applyMiddleware(...middlewares));
4.结语
在我看来redux是类似于session的状态保存器,整个模型是个不可逆的环形跑道,view->action->reducer->store->view(根据newStore更新ui),很简单的理念核心是不可逆,这个特性保证了数据的唯一性,reducer的纯函数特性保证了数据的可靠性,这使得redux成为了一个不错的、稳定的、可靠的状态器。