博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React Native实现一个带筛选功能的搜房列表(2)
阅读量:6151 次
发布时间:2019-06-21

本文共 9571 字,大约阅读时间需要 31 分钟。

原文链接

在中,我们实现了一个下拉刷新和上拉加载更多的列表,那根据一般的开发步骤,接着应该就是进行网络请求,在网络请求之后更新列表数据和列表的刷新状态。

这篇文章会向大家介绍一下Redux的基本概念以及在页面中如何使用Redux进行状态管理。

文章中的代码都来自。开始之前,我们先看一下最终实现的效果

Redux概念

首先先简单介绍一下Redux的一些概念。Redux是JavaScript状态容器,提供可预测化的状态管理,其工作流程如下:

整个工作流程为:

  1. View需要订阅Store中的state;
  2. 操作View(点击了View上的一个按钮或者执行一个网络请求),发出Action;
  3. Store自动调用Reducer,并且传入两个参数(Old State和Action),Reducer会返回新的State,如果有Middleware,Store会将Old State和Action传递给Middleware,Middleware会调用Reducer 然后返回新的State;
  4. State一旦有变化,Store就会调用监听函数,来更新View;

Store

Store是存储state的容器,负责提供所有的状态。整个应用只能有一个Store,这么做的目的是为了让组件之间的通信更加简单。

在没有Store的情况下,组件之间需要通信就比较麻烦,如果一个父组件要将状态传递到子组件,就需要通过props一层一层往下传,一个子组件的状态发生改变并且要让父组件知道,则必须暴露一个事件出去才能通信。这就使得组件之间通信依赖于组件的层次结构。此时如果有两个平级的节点想要通信,就需要通过它们的父组件进行中转。 有了这个全局的Store之后,所有的组件变成了和Store进行通信。这样组件之间通信就会变少,当Store发生变化,对应的组件也能拿到相关的数据。当组件内部有时间触发Store的变化时,更新Store即可。这也就是所谓的单向数据流过程。

Store的职责如下:

  • 维持应用的state;
  • 提供getState()方法获取state;
  • 提供dispatch(action)方法更新state;
  • 通过subscribe(listener)注册监听器;
  • 通过subscribe(listener)返回的函数注销监听器。

Action

当我们想要更改store中的state时,我们便需要使用Action。Action是Store数据的唯一来源,每一次修改state便要发起一次Action。

Action可以理解为是一个Javascript对象。其内部必须包含一个type字段来表示将要执行的动作,除了 type字段外,Action的结构完全由自己决定。多数情况下,type字段会被定义成字符串常量。

Action举例:

{    type: Types.SEARCH_HOUSE_LOAD_DATA_SUCCESS,    currentPage: ++currentPage,    houseList,    hasMoreData,}复制代码

Action创建函数

Action创建函数就是生成action的方法。“action” 和 “action 创建函数” 这两个概念很容易混在一起,使用时最好注意区分。

Action创建函数举例:

export function init(storeName) {    return dispatch => {        dispatch({ type: Types.HOUSE_DETAIL_INIT, storeName });    }}复制代码

Reducer

Store收到Action以后,必须给出一个新的State,这样View才会发生变化。 这种State的计算过程就叫做Reducer。Reducer是一个纯函数,它只接受Action和当前State作为参数,返回一个新的State。

由于Reducer是一个纯函数,所以我们不能在reducer里执行以下操作:

  • 修改传入的参数;
  • 执行有副作用的操作;
  • 调用非纯函数;
  • 不要修改state;
  • 遇到未知的action时,一定要返回旧的state;

Reducer举例:

const defaultState = {    locationCityName: '',    visitedCities: [],    hotCities: [],    sectionCityData: [],    sectionTitles: []};export function cityListReducer(state = defaultState, action) {    switch (action.type) {        case Types.CITY_LIST_LOAD_DATA:            return {                ...state,                visitedCities: action.visitedCities,                hotCities: action.hotCities,                sectionCityData: action.sectionCityData,                sectionTitles: action.sectionTitles,            }        case Types.CITY_LIST_START_LOCATION:        case Types.CITY_LIST_LOCATION_FINISHED:            return {                ...state,                locationCityName: action.locationCityName            };        default:            return state;    }}复制代码

拆分与合并reducer

在开发过程中,由于有的功能是相互独立的,所以我们需要拆分reducer。一般情况下,针对一个页面可以设置一个reducer。但redux原则是只允许一个根reducer,接下来我们需要将每个页面的的reducer聚合到一个根reducer中。

合并reducer代码如下:

const appReducers = combineReducers({    nav: navReducer,    home: homeReducer,    cityList: cityListReducer,    apartments: apartmentReducer,    houseDetails: houseDetailReducer,    searchHouse: searchHouseReducer,});export default (state, action) => {    switch (action.type) {        case Types.APARTMENT_WILL_UNMOUNT:            delete state.apartments[action.storeName];            break;        case Types.HOUSE_DETAIL_WILL_UNMOUNT:            delete state.houseDetails[action.storeName];            break;        case Types.SEARCH_HOUSE_WILL_UNMOUNT:                delete state.searchHouse;            break;    }    return appReducers(state, action);}复制代码

SearchHousePage使用Redux

Action类型定义

SEARCH_HOUSE_LOAD_DATA: 'SEARCH_HOUSE_LOAD_DATA',SEARCH_HOUSE_LOAD_MORE_DATA: 'SEARCH_HOUSE_LOAD_MORE_DATA',SEARCH_HOUSE_LOAD_DATA_SUCCESS: 'SEARCH_HOUSE_LOAD_DATA_SUCCESS',SEARCH_HOUSE_LOAD_DATA_FAIL: 'SEARCH_HOUSE_LOAD_DATA_FAIL',SEARCH_HOUSE_WILL_UNMOUNT: 'SEARCH_HOUSE_WILL_UNMOUNT',复制代码

Action创建函数

export function loadData(params, currentPage, errorCallBack) {    return dispatch => {        dispatch({ type: currentPage == 1 ? Types.SEARCH_HOUSE_LOAD_DATA : Types.SEARCH_HOUSE_LOAD_MORE_DATA });        setTimeout(() => {            Network                .my_request({                    apiPath: ApiPath.SEARCH,                    apiMethod: 'searchByPage',                    apiVersion: '1.0',                    params: {                        ...params,                        pageNo: currentPage,                        pageSize: 10                    }                })                .then(response => {                    const tmpResponse = AppUtil.makeSureObject(response);                    const hasMoreData = currentPage < tmpResponse.totalPages;                    const houseList = AppUtil.makeSureArray(tmpResponse.resultList);                    dispatch({                        type: Types.SEARCH_HOUSE_LOAD_DATA_SUCCESS,                        currentPage: ++currentPage,                        houseList,                        hasMoreData,                    });                })                .catch(error => {                    if (errorCallBack) errorCallBack(error.message);                    const action = { type: Types.SEARCH_HOUSE_LOAD_DATA_FAIL };                    if (currentPage == 1) {                        action.houseList = []                        action.currentPage = 1;                    };                    dispatch(action);                });        }, 300);    }}复制代码

创建reducer

// 默认的stateconst defaultState = {    houseList: [],    headerIsRefreshing: false,    footerRefreshState: FooterRefreshState.Idle,    currentPage: 1,}export function searchHouseReducer(state = defaultState, action) {    switch (action.type) {        case Types.SEARCH_HOUSE_LOAD_DATA: {            return {                ...state,                headerIsRefreshing: true            }        }        case Types.SEARCH_HOUSE_LOAD_MORE_DATA: {            return {                ...state,                footerRefreshState: FooterRefreshState.Refreshing,            }        }        case Types.SEARCH_HOUSE_LOAD_DATA_FAIL: {            return {                ...state,                headerIsRefreshing: false,                footerRefreshState: FooterRefreshState.Failure,                houseList: action.houseList ? action.houseList : state.houseList,                currentPage: action.currentPage,            }        }        case Types.SEARCH_HOUSE_LOAD_DATA_SUCCESS: {            const houseList = action.currentPage <= 2 ? action.houseList : state.houseList.concat(action.houseList);            let footerRefreshState = FooterRefreshState.Idle;            if (AppUtil.isEmptyArray(houseList)) {                footerRefreshState = FooterRefreshState.EmptyData;            } else if (!action.hasMoreData) {                footerRefreshState = FooterRefreshState.NoMoreData;            }            return {                ...state,                houseList,                currentPage: action.currentPage,                headerIsRefreshing: false,                footerRefreshState,            }        }        default:            return state;    }}复制代码

包装组件

class SearchHousePage extends Component {    // ...代码省略    componentDidMount() {        this._loadData(true);    }    componentWillUnmount() {        NavigationUtil.dispatch(Types.SEARCH_HOUSE_WILL_UNMOUNT);    }    _loadData(isRefresh) {        const { loadData, searchHouse } = this.props;        const currentPage = isRefresh ? 1 : searchHouse.currentPage;        loadData(this.filterParams, currentPage, error => Toaster.autoDisapperShow(error));    }        render() {        const { home, searchHouse } = this.props;        return (            
`${item.id}`} renderItem={({ item, index }) => this._renderHouseCell(item, index)} headerIsRefreshing={searchHouse.headerIsRefreshing} footerRefreshState={searchHouse.footerRefreshState} onHeaderRefresh={() => this._loadData(true)} onFooterRefresh={() => this._loadData(false)} footerRefreshComponent={footerRefreshState => this.footerRefreshComponent(footerRefreshState, searchHouse.houseList)} />
NavigationUtil.goBack()} title='搜房' />
this._loadData(true)} onUpdateParameters={({ nativeEvent: { filterParams } }) => { this.filterParams = { ...this.filterParams, ...filterParams, }; }} />
); }}const mapStateToProps = state => ({ home: state.home, searchHouse: state.searchHouse });const mapDispatchToProps = dispatch => ({ loadData: (params, currentPage, errorCallBack) => dispatch(loadData(params, currentPage, errorCallBack)),});export default connect(mapStateToProps, mapDispatchToProps)(SearchHousePage);复制代码

从上面的代码使用了一个connect函数,connect连接React组件与Redux store,连接操作会返回一个新的与Redux store连接的组件类,并且连接操作不会改变原来的组件类。

mapStateToProps中订阅了home节点和searchHouse节点,该页面主要使用searchHouse节点,那订阅home节点是用来方便组件间通信,这样页面进行网络请求所需的cityId,就不需要从前以页面传入,也不需要从缓存中读取。

列表的刷新状态由headerIsRefreshingfooterRefreshState进行管理。

综上

redux已经帮我们完成了页面的状态管理,再总结一下Redux需要注意的点:

  • Redux应用只有一个单一的Store。当需要拆分数据处理逻辑时,你应该使用拆分与合并reducer而不是创建多个Store;
  • redux一个特点是:状态共享,所有的状态都放在一个Store中,任何组件都可以订阅Store中的数据,但是不建议组件订阅过多Store中的节点;
  • 不要将所有的State都适合放在Store中,这样会让Store变得非常庞大;

到这里,我们实现了列表的下拉刷新、加载更多以及如何使用redux,还差一个筛选栏和子菜单页面的开发,这里涉及到React Native与原生之间的通信,我会在中分享下如何进行React Native与原生的桥接开发。

另外上面提供的代码均是从项目当中截取的,如果需要查看完整代码的话,在中。

上述相关代码路径:

redux文件夹: /NNHybridRN/reduxSearchHousePage: /NNHybridRN/sections/searchHouse/SearchHousePage.js复制代码

参考资料:

转载地址:http://rlzfa.baihongyu.com/

你可能感兴趣的文章
[LeetCode]65.Valid Number
查看>>
事务隔离级别小记
查看>>
细谈Ehcache页面缓存的使用
查看>>
String.format详解
查看>>
第1章 maven概览及快速入门
查看>>
五大理由分配你的告警
查看>>
如何创建电报机器人
查看>>
React系列-Redux中间件
查看>>
我对原型链的学习理解笔记
查看>>
Java面试那些道道——今年秋招就靠它了
查看>>
http2.0的新功能
查看>>
Percona Toolkit初识
查看>>
git 恢复被删除的文件
查看>>
[译]HTML&CSS Lesson3: 了解CSS
查看>>
Spring Boot 视频
查看>>
slor6.6 在linux下的安装以及启动失败解决办法
查看>>
简单的BBS论坛 数据库设计
查看>>
vue2.0自定义指令,用touch事件替换scroll事件
查看>>
windows系统下在dos命令行kill掉被占用的pid
查看>>
使用 flutter 启动系统桌面
查看>>