React NativeReact-Native开发大全ReactNative

redux与react-navigation的集成

2017-05-21  本文已影响6044人  你好啊憨憨米

1. 闲扯一发

作为一个技术小宅男,周末还是适合待在家里,总结总结在前端路上遇到过的各种技术。今天呢,咱接着上周的话题,继续扯扯react-native相关的事情。在react-native的开发中,感觉最难管理,同时也最需要管理的就是各种state和router了。截止目前为止,Github上最火的state管理库非redux莫属,至于路由管理的话,还是喜欢官方推荐的由社区开发和维护的react-navigation,接下来呢,咱就一起来扯扯他俩的集成。

2. redux

redux是什么,简单点来说呢,就是一个管理state的库,但咱不能这么随便啊,还是把官网的定义给搬过来吧。
Redux 是 JavaScript 状态容器,提供可预测化的状态管理。可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。不仅于此,它还提供 超爽的开发体验,Redux 除了和 React 一起用外,还支持其它界面库。它体小精悍(只有2kB,包括依赖)。
。。。

2.1 redux的三项基本原则
2.2 Action

action 是把数据从应用传到store的有效载荷,是store数据的唯一来源。action为一个普通对象,这里建议按照Flux 标准 Action来定义。action 一般具有下面四个属性

// 正常
{
  type: 'ADD_TODO',
  payload: {
    text: 'Do something.'  
  },
  meta: {
     searchParms: {}
  }
}
//出错
{
  type: 'ADD_TODO',
  error: true,
  payload: new Error('error object'),
  meta: {
     searchParms: {}
  }
}
2.3 Action Creator

Action Creator就是生成 action 的方法,一般将action creator 的执行结果传递给 dispatch. 为了便于管理,这里建议使用第三方库redux-actions. 该库采用了Flux 标准的action定义。

import { createAction } from 'redux-actions';

let increment = createAction('INCREMENT', amount => amount);
// same as
increment = createAction('INCREMENT');

expect(increment(42)).to.deep.equal({
  type: 'INCREMENT',
  payload: 42
});
2.4 Reducer

Action只是描述了有事情发生了这一事实,并没有指明应用如何更新 state。而这正是 reducer 要做的事情。reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。这里呢,我还是建议采用redux-actions 来生成 reducer.

import { handleAction } from 'redux-actions';

var reducer = handleAction('FETCH_DATA', {
  next(state, action) {...},
  throw(state, action) {...}
}, defaultState);
2.5 Store

Store 就是把action和reducer联系到一起的对象。Store 有以下职责:

创建store的时候,我们使用 combineReducers 将多个 reducer 合并成为一个,并传递给 createStore 方法。

2.6 数据流

1 用户交互触发actionCreator的执行生成action
2 调用store的dispatch方法,触发reducer的执行
3 根据传入的action调用对应的reducer,更新store
4 触发组件props的改变从而引起组件的更新

redux的具体细节, 可以参考开发文档

3. React Navigation

React Navigation 是react native社区主推的一个导航器方案,Facebook官方建议使用其来进行导航的维护。
React Navigation的路由写法使其非常容易扩展导航逻辑,或是整合到redux中。由于路由可以嵌套使用,因而开发者可以根据不同页面编写不同的导航逻辑,且彼此互不影响。
具体的细节,这里我就不做特别的阐述了,大家可以直接阅读官方文档

4 redux与react-navigation的集成

前面我们已经介绍了redux和react-navigation的基本知识,接下来,就进入我们今天的主题,实现redux与react-navigation的集成。

4.1 效果预览

戳我查看效果哦。。。

4.2 整体目录

在项目根目录下建立src文件夹用于存放各种scripts和styles代码。

scripts目录具体细节如下

4.3 Actions

在actions目录下建立两个文件夹actionTypes和news。actionTypes用于存放action的type, news用于存放和news相关的action creator。

//actionTypes
export const FETCH_NEWS_LIST = 'FETCH_NEWS_LIST';

//news action creator
import {
    FETCH_NEWS_LIST
} from '../actionTypes';

import { createAction } from 'redux-actions';

const thumbnail = 'https://facebook.github.io/react/img/logo_og.png';
// 获取news 列表数据
export var fetchNewsList = createAction(FETCH_NEWS_LIST, () => {
    return [1,2,3,4,5,6,7,8,9,10].map(item => {
        return {
            id: item,
            title: `[${item}]夏季又要到,做好防脱准备很重要`,
            thumbnail: thumbnail,
            desc: '室内干燥,室外气温高,春夏季对于需要带状的上班族来说就是压力山大。'
        }
    });
});
4.4 Reducer

在reducers目录下建立一个news文件夹,用于存放和news相关的reducer。

/**
* 这里以news的为例,还有其他的reducer,具体的可以参考样例完整代码
*/
import {
    FETCH_NEWS_LIST
} from '../../actions/actionTypes';

import { handleActions } from 'redux-actions';

export default handleActions({
    [FETCH_NEWS_LIST]: {
        next(state, action) {
            return { ret: true, data: action.payload };
        },
        throw(state, action) {
            return { ret: false, statusText: action.payload, data: [] };
        }
    }
}, { ret: true, statusText: '', data: [] });

接下来在reducers目录下建立一个index.js文件,用于组合各种reducer

import { combineReducers } from 'redux';

import news from './news';
import categries from './category';

//和导航相关的reducer通过从调用出传递进来
export default function getReducers(navReducer) {
    return combineReducers({
        news,
        categries,
        nav: navReducer
    });
}
4.5 Store

在store文件夹下建立一个index.js文件,用于存放store相关的数据

import { createStore, applyMiddleware } from 'redux';
import promiseMiddleware from 'redux-promise';

import getReducers from '../reducers';
//promiseMiddleware 是异步action的一个中间件,本例子中暂时没有使用
export default function getStore(navReducer) {
    return createStore(
        getReducers(navReducer),
        undefined,
        applyMiddleware(promiseMiddleware)
    );
}
4.6 components

在components目录下建立Home文件夹,用于存放首页的业务组件,除了首页之外,还提供以下业务组件。

接下来,我们看看Home的代码

import React, { Component } from 'react';
import Icon from 'react-native-vector-icons/Ionicons';
import {
    View,
    Text,
    Image,
    FlatList,
    StatusBar,
    TouchableHighlight
} from 'react-native';
import { common, topicCard } from '../../../styles';
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import * as newsActions from '../../actions/news';

class Home extends Component {
    //定义底部tabBar的icon和name
    static navigationOptions = {
        tabBarLabel: '首页',
        tabBarIcon: ({ tintColor }) => (
            <Icon name="md-home" size={24} color={tintColor} />
        )
    };
    componentDidMount() {
        this.props.fetchNewsList();
    }
    _onPressItem = (id) => {
        this.props.navigation.navigate('NewsDetail', { id: id });
    };
    _keyExtractor = (item, index) => item.id;
    _renderItem = ({item}) => (
        <View style={topicCard.card}>
            <Image style={topicCard.cover} source={{uri: item.thumbnail}}></Image>
            <TouchableHighlight
                style={topicCard.title}
                onPress={this._onPressItem.bind(this, item.id)}>
                <Text numberOfLines={1} style={topicCard.titleText}>{item.title}</Text>
            </TouchableHighlight>
            <View style={topicCard.desc}>
                <Text numberOfLines={2} style={topicCard.descText}>{item.desc}</Text>
            </View>
        </View>
    );
    render() {
        return (
            <View style={[common.containerFixHeight, common.springWoodBg]}>
                <FlatList
                    data={this.props.news.data}
                    refreshing={false}
                    keyExtractor={this._keyExtractor}
                    renderItem={this._renderItem}
                  />
            </View>
        );
    }
}
//让业务组件和redux建立关联
export default connect(
    state => ({
        news: state.news
    }),
    dispatch => bindActionCreators(newsActions, dispatch)
)(Home);

在代码的结尾处,我们调用了react-redux的connect方法,让store, action 和 业务组件建立了关联,之后我们就可以在业务组件中通过this.props.news的方式来访问news相关的数据,以this.props.fetchNewsList() 的方式来触发请求news数据。

4.7 路由配置

和路由相关的配置,存放在routers文件夹里

import {
    TabNavigator,
    StackNavigator,
    addNavigationHelpers
} from "react-navigation";

import Home from '../components/Home';
import Category from '../components/Category';
import Card from '../components/Card';
import UserCenter from '../components/UserCenter';
import NewsDetail from '../components/NewsDetail';

//底部的tabBar导航
const TabbarNavigator = TabNavigator({
    Home: { screen: Home },
    Category: { screen: Category },
    Card: { screen: Card },
    UserCenter: { screen: UserCenter }
}, {
    initialRouteName: 'Home'
});
//整个应用的路由栈
const AppNavigator = StackNavigator({
    TabBar: {
        screen: TabbarNavigator,
        navigationOptions: {
            header: null
        }
    },
    NewsDetail: {
        path: 'news/:id',
        screen: NewsDetail
    }
});

export {
    AppNavigator
};

4.8 根组件

在上面的一切准备就绪之后,我们需要建立一个Root组件来实现redux,react-navigation以及业务组件的衔接。

import React, { Component } from "react";
import { Provider, connect } from "react-redux";
import { addNavigationHelpers } from "react-navigation";

import getStore from "./store";
import { AppNavigator } from './routers';

const navReducer = (state, action) => {
    const newState = AppNavigator.router.getStateForAction(action, state);
    return newState || state;
};

const mapStateToProps = (state) => ({
    nav: state.nav
});

class App extends Component {
    render() {
        return (
            <AppNavigator
                navigation={addNavigationHelpers({
                    dispatch: this.props.dispatch,
                    state: this.props.nav
                })}
            />
        );
    }
}

const AppWithNavigationState = connect(mapStateToProps)(App);

const store = getStore(navReducer);

export default function Root() {
    return (
        <Provider store={store}>
            <AppWithNavigationState />
        </Provider>
    );
}

接下来,我们只需要将根组件引入到整个应用的入口文件就ok啦,完美。


import {
  AppRegistry,
} from 'react-native';

import Root from './src/scripts/Root.js';

AppRegistry.registerComponent('reduxReactNavigation', () => Root);

5. 总结一发

至此,所有的介绍结束了,感谢你的耐心阅读,如果你想要了解更多,你可以直接查看完整的demo,也可以留言提issue。

上一篇下一篇

猜你喜欢

热点阅读