自学React-native (第八天)-- redux使用

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.jsApp的根文件,这里需要注意的是<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模块但是其实是使用renducermiddleware(中间件)生成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成为了一个不错的、稳定的、可靠的状态器。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值