react-native,react-redux和redux配合开发

简介: react-native 的数据传递是父类传递给子类,子类通过this.props.** 读取数据,这样会造成组件多重嵌套,于是用redux可以更好的解决了数据和界面View之间的关系, 当然用到的是react-redux,是对redux的一种封装。

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


有需要的交流的可以加个好友




相关文章
|
1月前
|
设计模式 存储 前端开发
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
React开发设计模式及原则概念问题之自定义Hooks的作用是什么,自定义Hooks设计时要遵循什么原则呢
|
2月前
|
前端开发 JavaScript 安全
TypeScript在React Hooks中的应用:提升React开发的类型安全与可维护性
【7月更文挑战第17天】TypeScript在React Hooks中的应用极大地提升了React应用的类型安全性和可维护性。通过为状态、依赖项和自定义Hooks指定明确的类型,开发者可以编写更加健壮、易于理解和维护的代码。随着React和TypeScript的不断发展,结合两者的优势将成为构建现代Web应用的标准做法。
|
30天前
|
存储 JavaScript 前端开发
React中使用redux
React中使用redux
127 56
|
23天前
|
存储 JavaScript 前端开发
react redux 实现原理
【8月更文挑战第29天】react redux 实现原理
17 4
|
22天前
|
JavaScript 前端开发 安全
[译] 使用 TypeScript 开发 React Hooks
[译] 使用 TypeScript 开发 React Hooks
|
1月前
|
移动开发 前端开发 JavaScript
"跨界大战!React Native、Weex、Flutter:三大混合开发王者正面交锋,揭秘谁才是你移动应用开发的终极利器?"
【8月更文挑战第12天】随着移动应用开发的需求日益增长,高效构建跨平台应用成为关键。React Native、Weex与Flutter作为主流混合开发框架各具特色。React Native依托Facebook的强大支持,以接近原生的性能和丰富的组件库著称;Weex由阿里巴巴开发,性能优越尤其在大数据处理上表现突出;Flutter则凭借Google的支持及独特的Dart语言和Skia渲染引擎,提供出色的定制能力和开发效率。选择时需考量项目特性、团队技能及生态系统的成熟度。希望本文对比能助你做出最佳决策。
105 1
|
1月前
|
前端开发
React——开发调式工具安装【五】
React——开发调式工具安装【五】
23 0
React——开发调式工具安装【五】
|
20天前
|
前端开发 Java UED
瞬间变身高手!JSF 与 Ajax 强强联手,打造极致用户体验的富客户端应用,让你的应用焕然一新!
【8月更文挑战第31天】JavaServer Faces (JSF) 是 Java EE 标准的一部分,常用于构建企业级 Web 应用。传统 JSF 应用采用全页面刷新方式,可能影响用户体验。通过集成 Ajax 技术,可以显著提升应用的响应速度和交互性。本文详细介绍如何在 JSF 应用中使用 Ajax 构建富客户端应用,并通过具体示例展示 Ajax 在 JSF 中的应用。首先,确保安装 JDK 和支持 Java EE 的应用服务器(如 Apache Tomcat 或 WildFly)。
27 0
|
20天前
|
前端开发 JavaScript Android开发
React Native 快速入门简直太棒啦!构建跨平台移动应用的捷径,带你开启高效开发之旅!
【8月更文挑战第31天】React Native凭借其跨平台特性、丰富的生态系统及优异性能,成为移动应用开发的热门选择。它允许使用JavaScript和React语法编写一次代码即可在iOS和Android上运行,显著提升开发效率。此外,基于React框架的组件化开发模式使得代码更加易于维护与复用,加之活跃的社区支持与第三方库资源,加速了应用开发流程。尽管作为跨平台框架,React Native在性能上却不输原生应用,支持原生代码优化以实现高效渲染与功能定制。对于开发者而言,React Native简化了移动应用开发流程,是快速构建高质量应用的理想之选。
29 0
|
20天前
|
前端开发 JavaScript 数据管理
React Formik入门:简化表单处理的神器——全面掌握Formik在React表单开发中的高效应用与实战技巧
【8月更文挑战第31天】在React应用中,表单处理常常因繁琐而令人头疼。Formik作为一个开源库,专为简化React表单设计,减少冗余代码并提升处理效率。本文介绍Formik的使用方法及其优势,通过示例展示如何安装配置并创建基本表单,帮助开发者轻松应对各种表单需求。
24 0