react-native 的数据传递是父类传递给子类,子类通过this.props.** 读取数据,这样会造成组件多重嵌套,于是用redux可以更好的解决了数据和界面View之间的关系, 当然用到的是react-redux,是对redux的一种封装。
react基础的概念包括:
1.action是纯声明式的数据结构,只提供事件的所有要素,不提供逻辑,同时尽量减少在 action 中传递的数据
2. reducer是一个匹配函数,action的发送是全局的,所有的reducer都可以捕捉到并匹配与自己相关与否,相关就拿走action中的要素进行逻辑处理,修改store中的状态,不相关就不对state做处理原样返回。reducer里就是判断语句
3.Store 就是把以上两个联系到一起的对象,Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用reducer组合 而不是创建多个 store。
4.Provider是一个普通组件,可以作为顶层app的分发点,它只需要store属性就可以了。它会将state分发给所有被connect的组件,不管它在哪里,被嵌套多少层
5.connect一个科里化函数,意思是先接受两个参数(数据绑定mapStateToProps和事件绑mapDispatchToProps)再接受一个参数(将要绑定的组件本身)。mapStateToProps:构建好Redux系统的时候,它会被自动初始化,但是你的React组件并不知道它的存在,因此你需要分拣出你需要的Redux状态,所以你需要绑定一个函数,它的参数是state,简单返回你需要的数据,组件里读取还是用this.props.*
6.container只做component容器和props绑定, 负责输入显示出来,component通过用户的要交互调用action这样就完整的流程就如此
来张图
流程如上,那么结构如下。
需要实现的效果如下, 顶部 一个轮播,下面listview,底部导航切换,数据来源豆瓣电影
程序入口
-
import React, { Component } from 'react';
-
import { AppRegistry } from 'react-native';
-
import App from './app/app';
-
export default class movies extends Component {
-
render() {
-
return(
-
<App/>
-
);
-
}
-
}
-
AppRegistry.registerComponent('moives', () => moives)
store 和数据初始化
-
/**
-
* @author ling
-
* @email helloworld3q3q@gmail.com
-
* @create date 2017-05-17 10:38:09
-
* @modify date 2017-05-17 10:38:09
-
* @desc [description]
-
*/
-
import { createStore, applyMiddleware, compose } from 'redux';
-
import { Provider } from 'react-redux';
-
import thunk from 'redux-thunk'
-
import React, { Component } from 'react';
-
import Root from './containers/root';
-
import allReducers from './reducers/allReducers';
-
import { initHotshow, fetchLoading } from './actions/hotshow-action';
-
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
-
const store = createStoreWithMiddleware(allReducers);
-
//初始化 进入等待 首屏数据 ajax请求
-
store.dispatch(fetchLoading(true));
-
class App extends Component {
-
constructor(props) {
-
super(props);
-
}
-
render() {
-
return (
-
<Provider store={ store }>
-
<Root/>
-
</Provider>
-
);
-
}
-
}
-
module.exports = App;
action纯函数,同时把action type单独写出来。在action同目录下文件types.js
-
/**
-
* @author ling
-
* @email helloworld3q3q@gmail.com
-
* @create date 2017-05-17 10:36:44
-
* @modify date 2017-05-17 10:36:44
-
* @desc [description]
-
*/
-
'use strict';
-
//首页 正在上映
-
export const HOTSHOW_BANNER = 'HOTSHOW_BANNER';
-
export const HOTSHOW_LIST = 'HOTSHOW_LIST';
-
export const HOTSHOW_FETCH = 'HOTSHOW_FETCH';
-
export const ADDMORE = 'AddMORE';
hotshow-action.js
-
/**
-
* @author ling
-
* @email helloworld3q3q@gmail.com
-
* @create date 2017-05-12 04:56:43
-
* @modify date 2017-05-12 04:56:43
-
* @desc [description]
-
*/
-
import { HOTSHOW_BANNER, HOTSHOW_LIST, HOTSHOW_FETCH, ADDMORE } from './types';
-
import { hotshowFetch } from '../middleware/index-api';
-
export const addBanner = (data) => {
-
return {
-
type: HOTSHOW_BANNER,
-
data
-
}
-
}
-
//加载等待,true 显示 反之
-
export const fetchLoading = (bool) => {
-
return {
-
type: HOTSHOW_FETCH,
-
bool
-
}
-
}
-
export const addList = (data) => {
-
return {
-
type: HOTSHOW_LIST,
-
data
-
}
-
}
-
// 正在热映 初始请求
-
export const initHotshow = () => {
-
return hotshowFetch(addList);
-
}
allReducers.js 整合所有reducer
-
import { combineReducers } from 'redux';
-
import { HotShowList, Banner, fetchLoading } from './hotshow/reducers'
-
const allReducers = combineReducers({
-
hotshows: HotShowList, // 首屏数据列表 listview
-
banner: Banner, // 轮播
-
fetchload: fetchLoading, //加载中boo
-
});
-
export default allReducers;
hotshowreducer
-
/**
-
* @author ling
-
* @email helloworld3q3q@gmail.com
-
* @create date 2017-05-12 04:56:34
-
* @modify date 2017-05-12 04:56:34
-
* @desc [description]
-
*/
-
import { HOTSHOW_BANNER, HOTSHOW_LIST, HOTSHOW_FETCH } from '../../actions/types';
-
export const HotShowList = (state = {}, action) => {
-
switch (action.type) {
-
case HOTSHOW_LIST:
-
return Object.assign(
-
{} , state , {
-
data : action.data
-
});
-
default:
-
return state;
-
}
-
}
-
export const Banner = (state = {}, action) => {
-
switch (action.type) {
-
case HOTSHOW_BANNER:
-
let subjects = action.data;
-
let data = subjects.slice(0, 5);// 前五个
-
return Object.assign(
-
{} , state , {
-
data : data
-
});
-
default:
-
return state;
-
}
-
}
-
export const fetchLoading = (state = {}, action) => {
-
switch (action.type) {
-
case HOTSHOW_FETCH:
-
return Object.assign(
-
{} , state , {
-
data : action.bool
-
});
-
default:
-
return state;
-
}
-
}
api 数据请求
-
/**
-
* @author ling
-
* @email helloworld3q3q@gmail.com
-
* @create date 2017-05-16 08:34:36
-
* @modify date 2017-05-16 08:34:36
-
* @desc [description]
-
*/
-
//const hotshow = 'https://api.douban.com/v2/movie/in_theaters';
-
// const sonshow = 'https://api.douban.com/v2/movie/coming_soon';
-
// const usshow = 'https://api.douban.com/v2/movie/us_box';
-
// const nearcinemas = 'http://m.maoyan.com/cinemas.json';
-
const hotshow = 'http://192.168.×.9:8080/weixin/hotshow.json';
-
const sonshow = 'http://192.168.×.9:8080/weixin/sonshow.json';
-
const usshow = 'http://192.168.×.9:8080/weixin/usshow.json';
-
const nearcinemas = 'http://192.168.×.9:8080/weixin/nearcinemas.json';
-
import { initHotshow, fetchLoading } from '../actions/hotshow-action';
-
export function hotshowFetch(action) {
-
return (dispatch) => {
-
fetch(hotshow).then(res => res.json())
-
.then(json => {
-
dispatch(action(json));
-
dispatch(fetchLoading(false));
-
}).catch(msg => console.log('hotshowList-err '+ msg));
-
}
-
}
containers\hotshow\index
-
/**
-
* @author ling
-
* @email helloworld3q3q@gmail.com
-
* @create date 2017-05-17 10:44:56
-
* @modify date 2017-05-17 10:44:56
-
* @desc [description]
-
*/
-
import React, { Component } from 'react';
-
import { View, ScrollView } from 'react-native';
-
import { bindActionCreators } from 'redux';
-
import { connect } from 'react-redux';
-
import { size } from '../../util/style';
-
import HotShowList from './hotshow-list';
-
import Loading from '../../compoments/comm/loading'
-
import { fetchLoading, initHotshow } from '../../actions/hotshow-action';
-
class hotshow extends Component {
-
componentWillMount() {
-
let _that = this;
-
let time = setTimeout(function(){
-
//请求数据
-
_that.props.initHotshowAction();
-
clearTimeout(time);
-
}, 1500);
-
}
-
render() {
-
return (<View >
-
{this.props.fetchbool ? <Loading/> : <HotShowList/> }
-
</View>);
-
}
-
}
-
function mapStateToProps(state) {
-
return {
-
fetchbool: state.fetchload.data,
-
hotshows: state.hotshows.data
-
}
-
}
-
function macthDispatchToProps(dispatch) {
-
return bindActionCreators({
-
initHotshowAction: initHotshow,
-
}, dispatch);
-
}
-
export default connect(mapStateToProps, macthDispatchToProps)(hotshow);
BannerCtn 轮播用的swiper 插件, 但swiper加入 listview 有个bug就是图片不显示,结尾做答
-
import React, { Component } from 'react';
-
import { Text, StyleSheet, View, Image } from 'react-native';
-
import { connect } from 'react-redux';
-
import { bindActionCreators } from 'redux';
-
import Swiper from 'react-native-swiper';
-
import { addBanner } from '../../actions/hotshow-action';
-
import { size } from '../../util/style';
-
class BannerCtn extends Component {
-
render() {
-
let data = this.props.banner.data;
-
return (
-
<View style={{height: 200}}>
-
{ data !== undefined ?
-
<Swiper height={200} autoplay={true}>
-
{
-
data.map((item, i) => {
-
return ( <View key={i} style={{flex: 1, height:200}}>
-
<Image style={{flex: 1}} resizeMode='cover'
-
source={{uri: item.images.large}}/>
-
<Text style={style.title}> {item.title} </Text>
-
</View>)
-
})
-
}
-
</Swiper>: <Text>loading</Text>
-
}
-
</View>
-
);
-
}
-
}
-
function mapStateToProps(state) {
-
return {
-
banner: state.banner
-
}
-
}
-
let style = StyleSheet.create({
-
title: {
-
position: 'absolute',
-
width: size.width,
-
bottom: 0,
-
color: '#ffffff',
-
textAlign: 'right',
-
backgroundColor: 'rgba(230,69,51,0.25)'
-
}
-
})
-
export default connect(mapStateToProps)(BannerCtn);
hotshow-list
-
import React, { Component } from 'react';
-
import { Text, View, ListView, StyleSheet } from 'react-native';
-
import { connect } from 'react-redux';
-
import { bindActionCreators } from 'redux';
-
import { addBanner } from '../../actions/hotshow-action';
-
import Loading from '../../compoments/comm/loading';
-
import Item from '../../compoments/hotshow/item';
-
import Banner from './banner-ctn';
-
import Foot from '../../compoments/comm/foot';
-
class HotShowList extends Component {
-
constructor(props) {
-
super(props);
-
}
-
componentWillMount() {
-
//顶部轮播
-
let { hotshows, bannerAction } = this.props;
-
let subs = hotshows.data.subjects;
-
bannerAction(subs);
-
}
-
_renderList() {
-
let { hotshows } = this.props;
-
let ary = hotshows.data.subjects, subsAry = [], row=[];
-
row.push(<Banner/>);
-
for(let i = 0, item; item = ary[i++];) {
-
//一行两个
-
subsAry.push(
-
<Item key={i} rank={i} data={item}/>
-
);
-
if(subsAry.length == 2) {
-
row.push(subsAry);
-
subsAry = [];
-
}
-
}
-
return row;
-
}
-
_renderRow(data) {
-
return(
-
<View style={{marginTop: 1, flexWrap:'wrap', flexDirection: 'row', justifyContent: 'space-between'}}>{data}</View>
-
);
-
}
-
render() {
-
let ds = new ListView.DataSource({
-
rowHasChanged: (r1, r2) => r1 !== r2
-
});
-
let data = this._renderList();
-
this.state = {
-
dataSource: ds.cloneWithRows(data),
-
}
-
//removeClippedSubviews 处理 banner 图片不显示
-
return (
-
<View>
-
<View>
-
<ListView removeClippedSubviews={false} dataSource={this.state.dataSource} renderRow={this._renderRow}/>
-
</View>
-
<Foot/>
-
</View>
-
);
-
}
-
}
-
function mapStateToProps(state) {
-
return {
-
hotshows: state.hotshows
-
}
-
}
-
function macthDispatchToProps(dispatch) {
-
return bindActionCreators({ bannerAction: addBanner}, dispatch);
-
}
-
let style = StyleSheet.create({
-
listbox: {
-
marginBottom: 45,
-
}
-
});
-
export default connect(mapStateToProps, macthDispatchToProps)(HotShowList);
剩下 便是foot tab 和单个item的编写
-
/**
-
* @author ling
-
* @email helloworld3q3q@gmail.com
-
* @create date 2017-05-19 08:38:19
-
* @modify date 2017-05-19 08:38:19
-
* @desc [description]
-
*/
-
import React, { Component } from 'react';
-
import { Text, View, Image, StyleSheet } from 'react-native';
-
import { size } from '../../util/style';
-
const width = size.width/2-0.2;
-
class item extends Component{
-
render() {
-
let data = this.props.data;
-
return(
-
<View style={style.box}>
-
<Image resizeMode='cover' style={style.avatar} source={{uri:data.images.large}}/>
-
<View style={style.rank}>
-
<Text style={style.rankTxt}>Top{this.props.rank}</Text>
-
</View>
-
<View style={style.msgbox}>
-
<View style={style.msgrow}>
-
<Text style={style.msgrowl}>{data.title}</Text>
-
<Text style={style.msgrowr}>评分:{data.rating.average}</Text>
-
</View>
-
<View style={style.msgrow}>
-
<Text style={style.msgrowl}>
-
{data.genres.map((item, i)=> {
-
if(i > 1) return;
-
i == 1 ? null : item += ',';
-
return item;
-
})}
-
</Text>
-
<Text style={style.msgrowr}>观影人数:{data.collect_count}</Text>
-
</View>
-
</View>
-
</View>
-
);
-
}
-
}
-
let style = StyleSheet.create({
-
box: {
-
width: width,
-
paddingBottom: 1
-
},
-
avatar: {
-
flex: 1,
-
height: 260,
-
},
-
rank: {
-
position: 'absolute',
-
top: 0,
-
left: 0,
-
backgroundColor: 'rgba(255,164,51,0.6)',
-
paddingVertical: 1,
-
paddingHorizontal: 3,
-
borderBottomRightRadius: 4
-
},
-
rankTxt: {
-
fontSize: 12,
-
color: '#ffffff'
-
},
-
msgbox: {
-
position: 'absolute',
-
bottom: 1,
-
width: width,
-
paddingHorizontal: 2,
-
backgroundColor: 'rgba(230,69,51,0.5)',
-
},
-
msgrow: {
-
flex: 1,
-
flexDirection: 'row',
-
justifyContent: 'space-between',
-
},
-
msgrowl: {
-
fontSize: 12,
-
color: '#ffffff'
-
},
-
msgrowr: {
-
fontSize: 13,
-
color: '#ffffff'
-
}
-
});
-
module.exports = item;
到此一个react-native 配合redux 编程首屏显示就大体完成了,
Swiper Image 在ListView不显示在,解决如下,测试手机微android 4.4.4,有把react-native升级为0.44.2 亲测无效
-
constructor(props) {
-
super(props);
-
this.state={
-
visibleSwiper: false
-
}
-
}
-
render() {
-
let data = this.props.banner.data;
-
return (
-
<View style={{height: 200}}>
-
{ this.state.visibleSwiper ?
-
<Swiper/>: <Text>LOADING</Text>
-
}
-
</View>
-
);
-
}
-
componentDidMount() {
-
let time = setTimeout(() => {
-
this.setState({
-
visibleSwiper: true
-
});
-
clearTimeout(time);
-
}, 200);
-
}
关于初始ajax数据,可以在create store的时候获取数据构建初始状态,也可以在ComponentDidMount的时候去执行action发ajax, 上文写错了,github 纠正
github:
https://github.com/helloworld3q3q/react-native-redux-demo