原文链接React Native实现一个带筛选功能的搜房列表(2)

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

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

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

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

// 默认的state
const 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 (<View style={styles.container} ref='container'><RefreshFlatListref='flatList'style={{ marginTop: AppUtil.fullNavigationBarHeight + 44 }}showsHorizontalScrollIndicator={false}data={searchHouse.houseList}keyExtractor={item => `${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)}/><NavigationBarnavBarStyle={{ position: 'absolute' }}backOrCloseHandler={() => NavigationUtil.goBack()}title='搜房'/><SearchFilterMenustyle={styles.filterMenu}cityId={`${home.cityId}`}subwayData={home.subwayData}containerRef={this.refs.container}filterMenuType={this.params.filterMenuType}onChangeParameters={() => this._loadData(true)}onUpdateParameters={({ nativeEvent: { filterParams } }) => {this.filterParams = {...this.filterParams,...filterParams,};}}/></View>);}
}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实现一个带筛选功能的搜房列表(3)中分享下如何进行React Native与原生的桥接开发。

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

上述相关代码路径:

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

参考资料:

Redux 中文文档

转载于:https://juejin.im/post/5cf88bee6fb9a07ebd48c68f

React Native实现一个带筛选功能的搜房列表(2)相关推荐

  1. 如何用 React Native 创建一个iOS APP?(二)

    我们书接上文<如何用 React Native 创建一个iOS APP?>,继续来讲如何用 React Native 创建一个iOS APP.接下来,我们会涉及到很多控件. 1 AppRe ...

  2. 面试官:请手写一个带取消功能的延迟函数,axios 取消功能的原理是什么

    大家好,我是若川.最近组织了源码共读活动,感兴趣的可以点此加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步.同时极力推荐订阅我写的<学习源码整体架构系列> ...

  3. ESP8266本质是一个带wifi功能的单片机

    ESP8266本质是一个带wifi功能的单片机,是乐鑫科技开发的,怪不得百度搜ESP8266第一个结果是乐鑫科技的. 所以wifi数传里面本质是有个单片机的,不可能就是几块数电芯片就完成了. http ...

  4. react native 开发APP(六)网络请求,列表的使用

    官网 https://reactnative.cn/ 项目下载地址:https://github.com/hebiao6446/DemoProject 陆续更新中... 在移动端开发的时候,我们基本上 ...

  5. React Native 0.50版本新功能简介

    React Native在2017年经历了众多版本的迭代,从本人接触的0.29版本开始,到前不久发布的0.52版本,React Native作为目前最受欢迎的移动跨平台方案.虽然,目前存在着很多的功能 ...

  6. React Native 集成分享第三方登录功能分享第三方登录模块开发(iOS)

    期待已久的新课上线啦!解锁React Native开发新姿势,一网打尽React Native最新与最热技术,点我Get!!! 在我们常用的App中经常会看到分享与第三方登录的功能,可以说分享与第三方 ...

  7. android 筛选控件_Flutter学习六之实现一个带筛选的列表页面

    上期实现了一个网络轮播图的效果,自定义了一个轮播图组件,继承自StatefulWidget,我们知道Flutter中并没有像Android中activity的概念.页面间的跳转是通过路由从一个全屏组件 ...

  8. PHP实现列表页综合筛选功能,dede织梦列表页联动筛选方法功能的实现

    [DEDECMS.织梦开源程序已经成为市场上主流仿站开源代码,它能够非常快速的放置一个已经建立好的网站,但是前提是这些网站比较简单,没有一些复杂的功能,那么使用DEDECMS仿站是最快速,最有效的.而 ...

  9. 山寨风,高仿QQ附近的人筛选功能的滑动选择列表来袭!

    今天在准备新项目的界面,偶然翻到了QQ附近的人那个筛选功能,嗯,觉得效果还不错,效果大概是这样子的.QQ的原图我就不上了,我就上我做的效果图. 觉得so easy是吧,但是我整整做了4个多小时,个多小 ...

最新文章

  1. 提交调用验证_干货丨RPA验证码识别技巧
  2. SGU-176 Flow construction 有上下界的最小流
  3. Object类的hashCode()方法
  4. 剑指Offer - 面试题65. 不用加减乘除做加法(位运算,要看哦)
  5. java foreach 实现原理
  6. python sklearn生成分类、回归任务数据集(可选切分训练数据/测试数据)
  7. [ios] NSSet,NSMutableSet[zhuan]
  8. 数值优化(二):信赖域方法与二维空间法
  9. 如何禁用 MacBook 在打开盖子时自动启动功能
  10. 该来的还是要来,数据挖掘
  11. DIY 3D打印机测试
  12. java链式编程/级联式编程
  13. MAC CPU温度监视及风扇速度控制软件
  14. 计算机在条形码的应用,条码应用
  15. 软件开发中三员管理职责
  16. 概念区分:灰度发布、蓝绿发布、滚动发布
  17. cmd运行记事本java文件
  18. 文件服务器磁盘配额管理,文件服务器磁盘配额的管理.doc
  19. iphone7刷入linux,iPhone7怎么进入DFU模式 iPhone7刷机步骤【详解】
  20. 投资理财学习笔记二,1.4做个有钱人之富人思维

热门文章

  1. listrecord根据某个属性去重_天刀手游琅纹搭配攻略,全门派全装备最细节提属性教学...
  2. Java:Java 队列的遍历
  3. 毕业论文写作Tips
  4. 安装MongoDB [4.0.2版本]
  5. 解决IE浏览器登陆失败异常!
  6. Linux学习笔记——Ubuntu更新软件源
  7. 炫酷实用的jQuery插件 涵盖菜单、按钮、图片
  8. 【php】用filter_var实现的简单参数验证
  9. 《几何与代数导引》习题1.36.2
  10. Windows2003ServerEnterprise+Oracle11g+VMWorkstation7.1:在虚拟机下Windows系统