本文将讲解以下的知识点:
- 如何基于redux+FlatList实现列表页数据加载
- 如何设计最热模块的state树
- 如何操作异步action与数据流
- 如何动态的设置store和获取store
- 如何灵活应用connect
- action如何和调用的页面进行交互
- FlatList高级应用与加载更多的实现及优化
该页面主要实现的功能是用户打开或下拉刷新会触发一个action,这个action会进行异步action的操作再交给reducer,reducer会处理数据返回state给state树然后渲染当前页面。
实现步骤:
第一步:创建一个action来请求数据并且把数据给reducer
action/popular/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
import Types from '../types'; import DataStore from '../../expand/dao/DataStore'; /** * 加载最热数据 发送异步action * @param storeName * @param url * @returns {function(...[*]=)} */ export function onLoadPopularData(storeName, url) { return dispatch => { // 先发出一个action,这个action主要做一些准备,比如下拉的时候显示loading dispatch({type: Types.POPULAR_REFRESH, storeName:storeName}); // 开始异步操作 let dataStore = new DataStore(); dataStore.fetchData(url) // 异步action与数据流 .then(data=>{ handleData(dispatch, storeName, data); }) .catch(error => { console.log(error); dispatch({ type: Types.LOAD_POPULAR_FAIL, storeName, error, }); }); }; } function handleData(dispatch, storeName, data) { dispatch({ type: Types.LOAD_POPULAR_SUCCESS, items: data && data.data && data.data.items, storeName, }); } |
当初设计框架的时候这个action是一个子的action,需要在中的action里面导入合并一下:
action/index.js
1 2 3 4 5 6 7 |
import {onThemeChange} from './theme'; import {onLoadPopularData} from './popular'; export default { onThemeChange, onLoadPopularData, }; |
为了更好的管理action还需要加入一下这个action的type:
1 2 3 4 5 6 7 |
export default { THEME_CHANGE:'THEME_CHANGE', THEME_INIT:'THEME_INIT', POPULAR_REFRESH:'POPULAR_REFRESH', LOAD_POPULAR_FAIL:'LOAD_POPULAR_FAIL', LOAD_POPULAR_SUCCESS:'LOAD_POPULAR_SUCCESS', }; |
接下来创建reducer,建立一个子的reducer来处理返回的数据返回新的state树,这里有一个重点就是动态设置store的key
第二步:创建一个reducer来将接收的数据合成新的state树
reducer/popular/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
import Types from '../../action/types'; const defaultState = { theme: 'red', }; // 注意在reducer里面不能修改原来state,只能返回一个新的state /** * popular:{ * java:{ * items:[], * isLoading: false * }, * iso:{ * items:[], * isLoading:false * }, * } * @param state * @param action * @returns {{theme: string}|{theme: *}} */ export default function onAction(state = defaultState, action) { switch (action.type) { case Types.LOAD_POPULAR_SUCCESS: return { ...state, [action.storeName]:{ // 这里是重点,动态的设置store key ...[action.storeName], items:action.items, isLoading:false, }, }; case Types.POPULAR_REFRESH: return { ...state, [action.storeName]:{ ...[action.storeName], isLoading:true, }, }; case Types.LOAD_POPULAR_FAIL: return { ...state, [action.storeName]:{ ...[action.storeName], isLoading:false, }, }; default: return state; } } |
接下来在外面将reducer合并一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import {combineReducers} from 'redux'; import theme from './theme'; import popular from './popular'; /** * 3.合并reducer * @type {Reducer<CombinedState<{}>>} */ const index = combineReducers({ theme: theme, popular: popular }); export default index; |
第三步:最后在页面中进行数据的请求并且用FlatList展示出来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
import React, {Component} from 'react'; import {View, Text, StyleSheet, Button, FlatList, RefreshControl} from 'react-native'; import {createMaterialTopTabNavigator} from 'react-navigation-tabs'; import {createAppContainer} from 'react-navigation'; import {connect} from 'react-redux'; // connect使这个页面组件和state树进行关联 import actions from '../action/index'; const URL = 'https://api.github.com/search/repositories?q='; const QUERY_STR = '&sort=stars'; // 根据点赞数进行排序 export default class PopularPage extends Component{ constructor(props) { super(props); this.tabNames = ['Java', 'Android', 'iOS', 'React', 'React Navigation', 'PHP']; } _genTabs(){ const tabs = {}; this.tabNames.forEach((item, index)=>{ tabs[`tab${index}`] = { screen: props => <PopularTabPage {...this.props} tabLabel={item}/>, // 设置顶部导航器的重点 navigationOptions: { title: item, }, }; }); return tabs; } render(){ const TabNavigator = createAppContainer(createMaterialTopTabNavigator( this._genTabs(), { tabBarOptions:{ tabStyle: styles.tabStyle, upperCaseLabel: false, scrollEnabled: true, style: { backgroundColor: '#a67', }, indicatorStyle: styles.indicatorStyle, labelStyle: styles.labelStyle, }, } )); return ( <View style={styles.container}> <TabNavigator/> </View> ); } } // 选项卡组件 class PopularTab extends Component{ constructor(props) { super(props); const {tabLabel} = this.props; this.storeName = tabLabel; } componentDidMount(){ this.loadData(); } loadData(){ const {onLoadPopularData} = this.props; const url = this.genFetchUrl(this.storeName); onLoadPopularData(this.storeName, url); // console.log(this.props.onLoadPopularData) } genFetchUrl(key) { return URL + key + QUERY_STR; } renderItem = (data)=>{ const item = data.item; return <View style={{marginBottom: 10}}> <Text style={{backgroundColor: '#faa'}}> {JSON.stringify(item)} </Text> </View>; }; render() { const {popular} = this.props; let store = popular[this.storeName]; // 动态获取state if (!store){ store = { items: [], isLoading: false, }; } return ( <View style={styles.container}> <FlatList data={store.items} renderItem={data=>this.renderItem(data)} keyExtractor={item=>""+item.id} refreshControl={ <RefreshControl title={'Loading'} titleColor={'red'} colors={['red']} refreshing={store.isLoading} onRefresh={()=>this.loadData()} // 下拉刷新的时候出发操作 tintColor={'red'} /> } /> </View> ); } } const mapStateToProps = state => ({ popular: state.popular, }); const mapDispatchToProps = dispatch => ({ onLoadPopularData: (storeName, url) => dispatch(actions.onLoadPopularData(storeName, url)), test:'123', }); const PopularTabPage = connect(mapStateToProps, mapDispatchToProps)(PopularTab); const styles = StyleSheet.create({ container: { flex: 1, marginTop: 30, }, welcome:{ fontSize: 20, textAlign: 'center', margin: 10, }, tabStyle:{ minWidth: 50, }, indicatorStyle: { height:2, backgroundColor: 'white', }, labelStyle: { fontSize: 13, marginTop: 6, marginBottom: 6, }, }); |