React Native 开发豆瓣评分(八)首页开发
首页完成效果展示:
一、开发占位图组件
在没有数据的时候使用占位图替代 items 的位置。
在 components 目录里创建 moviesItemPlaceholder.js
import React, { Component } from 'react';
import { View, StyleSheet } from 'react-native';
import { px } from '../utils/device';
export default class MoviesItemPlaceholder extends Component {
render() {
const arr = [1, 2, 3, 4];
return (
<View style={styles.page}>
{arr.map((index) => (
<View style={styles.placeholder} key={index}>
<View style={styles.img} />
<View style={styles.title} />
<View style={styles.rate} />
</View>
))}
</View>
)
}
}
const styles = StyleSheet.create({
page: {
flexDirection: 'row',
paddingLeft: px(30)
},
placeholder: {
width: px(160),
marginRight: px(16)
},
img: {
width: px(160),
height: px(224),
overflow: 'hidden',
borderRadius: px(8),
backgroundColor: '#f8f8f8'
},
title: {
marginTop: px(20),
backgroundColor: '#f8f8f8',
height: px(30),
width: px(130),
overflow: 'hidden',
borderRadius: px(8)
},
rate: {
marginTop: px(16),
backgroundColor: '#f8f8f8',
height: px(24),
width: px(130),
overflow: 'hidden',
borderRadius: px(8)
}
});
二、首頁数据请求
使用 postman 之类的工具可以看到,首页接口返回的数据字段大致一样,数据均在 subject_collection_items 字段里,可以疯转一个方法量来请求数据。
var items = ['showing', 'hot', 'tv', 'variety', 'book', 'music'];
items.forEach(type => {
this.getList(type);
});
getList = (type) => {
ajax(type, {
start: 0,
count: 9
}).then(value => {
let state = {}
state[type] = value.subject_collection_items;
this.setState(state);
})
}
首页页面展示
纵向滑动,使用 ScrollView 组件;横向滑动,使用 FlatList 组件,FlatList 组件的 ListEmptyComponent 表示没有数据时显示的组件,在这里放置占位图组件;
import React from "react";
import { View, Text, StatusBar, StyleSheet, ScrollView, FlatList, TouchableWithoutFeedback } from "react-native";
import { connect } from 'react-redux';
import ajax from "../utils/ajax";
import Header from '../components/header';
import ItemsHeader from '../components/itemsHeader';
import MoviesItem from '../components/moviesItem';
import MoviesItemPlaceholder from '../components/moviesItemPlaceholder';
import Icon from 'react-native-vector-icons/AntDesign';
import { px } from "../utils/device";
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {
showing: [],
hot: [],
tv: [],
variety: [],
book: [],
music: [],
}
var items = ['showing', 'hot', 'tv', 'variety', 'book', 'music'];
items.forEach(type => {
this.getList(type);
});
}
getList = (type) => {
ajax(type, {
start: 0,
count: 9
}).then(value => {
let state = {}
state[type] = value.subject_collection_items;
this.setState(state);
})
}
render() {
const { dispatch, value, navigation } = this.props;
const { showing, hot, tv, variety, book, music } = this.state;
const sections = [
{ title: '影院热映', data: showing, type: 'showing' },
{ title: '豆瓣热门', data: hot, type: 'hot' },
{ title: '近期热门剧集', data: tv, type: 'tv' },
{ title: '近期热门综艺节目', data: variety, type: 'variety' },
{ title: '畅销图书', data: book, type: 'book' },
{ title: '热门单曲榜', data: music, type: 'music' }
]
return (
<View style={styles.page}>
<Header showBack={false} title='豆瓣评分' backgroundColor='#00b600' color='#fff' />
<ScrollView>
<View style={styles.search}>
<TouchableWithoutFeedback onPress={() => alert('search')}>
<View style={styles.searchView}>
<Icon name='search1' size={px(30)} color='#ccc' />
<Text style={styles.searchText}>搜索</Text>
</View>
</TouchableWithoutFeedback>
</View>
{sections.map((list, index) => (
<View key={index} style={styles.list}>
<ItemsHeader title={list.title} onPress={() => navigation.push('List', { data: list })} />
<FlatList
horizontal={true}
data={list.data}
keyExtractor={(item, index) => 'item' + index}
ListEmptyComponent={() => <MoviesItemPlaceholder />}
renderItem={({ item, index }) => (
<View style={{ marginRight: index !== showing.length - 1 ? px(16) : px(30), marginLeft: index === 0 ? px(30) : 0 }}>
<MoviesItem data={item} />
</View>
)}
/>
</View>
))}
</ScrollView>
</View>
);
}
}
const select = (store) => {
return {
value: store.num.value,
}
}
export default connect(select)(Home);
const styles = StyleSheet.create({
page: {
flex: 1,
backgroundColor: '#fff'
},
search: {
backgroundColor: '#00b600',
height: px(80),
alignItems: 'center',
justifyContent: 'center'
},
searchView: {
height: px(50),
width: px(710),
borderRadius: px(8),
backgroundColor: '#fff',
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center'
},
searchText: {
fontSize: px(26),
color: '#ccc',
marginLeft: px(6)
},
list: {
marginBottom: px(30)
}
});
四、缓存列表数据
当处于弱网环境时,打开应用,可能会显示很久的占位图,此时我们可以将列表数据缓存至本地,每次进入应用先展示本地缓存的数据,然后请求数据,替换本地数据。
此时,就可以使用 redux 了。
编译 reducer
为了目录整清晰点(让 redux 相关的代码文件都存储于 store 目录下),将 src 目录下的 reducer 目录移动到 store 目录下,并在 reducer 目录创建 list.js。
const initList = {
showing: [],
hot: [],
tv: [],
variety: [],
book: [],
music: []
}
const setListState = (state = initList, action) => {
switch (action.type) {
case 'showing':
return {
...state,
showing: action.data
}
case 'hot':
return {
...state,
hot: action.data
}
case 'tv':
return {
...state,
tv: action.data
}
case 'variety':
return {
...state,
showing: action.data
}
case 'book':
return {
...state,
book: action.data
}
case 'music':
return {
...state,
music: action.data
}
default:
return state;
}
}
export default setListState;
在 reducer 的 index.js 中导入 setListState;
...
import setListState from './list';
...
export default combineReducers({
...
list: setListState
...
});
修改 store/index.js 的路径引入
import reducer from './reducer';
编辑 action
将之前src下的 action 目录删除,在 store 目录下创建 action.js。
export function login(data) {
return {
type: 'login',
data
}
}
export function logout(data) {
return {
type: 'logout',
data
}
}
// set 首页列表数据
export function setShowing(data) {
return {
type: 'showing',
data
}
}
export function setHot(data) {
return {
type: 'hot',
data
}
}
export function setTv(data) {
return {
type: 'tv',
data
}
}
export function setVariety(data) {
return {
type: 'variety',
data
}
}
export function setBook(data) {
return {
type: 'book',
data
}
}
export function setMusic(data) {
return {
type: 'music',
data
}
}
编辑首页
导入修改 store 的方法:
import { setShowing, setHot, setTv, setVariety, setBook, setMusic } from '../store/action';
页面获取 store 存储的数据:
const select = (store) => {
return {
showing: store.list.showing,
hot: store.list.hot,
tv: store.list.tv,
variety: store.list.variety,
book: store.list.book,
music: store.list.music
}
}
export default connect(select)(Home);
页面获取数据时,改变 store 里的数据
由于需要 save 数据,所以前面创建的 getList 和传入的 items 需要做一些改变:
...
var items = [
{ type: 'showing', save: setShowing },
{ type: 'hot', save: setHot },
{ type: 'tv', save: setTv },
{ type: 'variety', save: setVariety },
{ type: 'book', save: setBook },
{ type: 'music', save: setmusic },
]
items.forEach(item => {
this.getList(item);
});
...
getList = (item) => {
ajax(item.type, {
start: 0,
count: 9
}).then(value => {
this.props.dispatch(item.save(value.subject_collection_items));
})
}
修改 render 里的获取数据方式:
render(){
const { dispatch, value, navigation, showing, hot, tv, variety, book, music } = this.props;
...
}
至此,首页算是开发完了。