使用 react-native 写的一个 ShopCart 的 demo
目录:
一、index.android.js
// 定义全局变量 global.__APP__ = true; // 用于区分是web应用还是app应用 便于代码多端调用 global.__ANDROID__ = true; // android环境中 global.__IOS__ = false; // 将所有代码放在src目录下执行,方便android和ios调用 require('./src'); // 如果写的是目录,则默认调用目录下小写的index.js文件
二、index.ios.js
// 定义全局变量 global.__APP__ = true; // 用于区分是web应用还是app应用 便于代码多端调用 global.__ANDROID__ = false; global.__IOS__ = true; // ios 环境中 // 将所有代码放在src目录下执行,方便android和ios调用 require('./src'); // 如果写的是目录,则默认调用目录下小写的index.js文件
三、Index.js ( 跳转页 )
import React, { Component } from 'react'; import { AppState, StyleSheet, View, Button, Text } from 'react-native'; const INITIAL_ROUTE = { location:'/splash', } const styles = StyleSheet.create({ root: { flex: 1, justifyContent: 'center', }, text: { textAlign: 'center', fontSize: 18, } }); // 导入ShopCart import ShopCart from './ShopCart'; export default class App extends Component { gotoShopCart = () => { // 通过解钩 获取传递的navigator const { navigator } = this.props; // push方法会经过navigator的renderScene方法 navigator.push({ component: ShopCart }); }; render() { return ( <View style={styles.root}> <Text style={styles.text}>这是首页</Text> <Button onPress={this.gotoShopCart}>点这里跳转到购物车</Button> </View> ); } }
四、ShopCart.js ( 购物车页面 )
/** * 购物车 */ import React, { Component } from 'react'; import { AppState, StyleSheet, View, Text } from 'react-native'; // 引入observer 使静态数据可读写 import { observer } from 'mobx-react/native'; // 引入改造后的数据 import cartData from '../logics/CartData'; // 引入Header import Header from '../components/Header'; // 引入ItemList import ItemList from '../components/ItemList'; // 引入Footer import Footer from '../components/Footer'; const styles = StyleSheet.create({ root: { flex: 1, }, }); export default class ShopCart extends Component { render() { // ShopCart具有navigator const { navigator } = this.props; return ( <View style={styles.root}> <Header navigator={navigator} /> <ItemList cartData={cartData} /> <Footer cartData={cartData} /> </View> ); } }
五、组件
1. Circle.js
/** * 勾选框组件 */ import React, { Component } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; const styles = StyleSheet.create({ select: { height: 20, 20, borderRadius: 10, borderColor: '#000', borderWidth: StyleSheet.hairlineWidth, }, checked: { backgroundColor: '#f23030', }, }); export default class Circle extends Component { select = () => { // 通过props,从父组件取出onPress const { onPress } = this.props; // 赋值 let { checked } = this.state; // 值相等的情况,es6的简写 checked:checked,只写一个checked, this.setState({ checked, }); // 判断onPress是否存在,存在则执行下面的代码 onPress && onPress(checked); }; state = { checked: false, }; render() { <TouchableOpacity style={[styles.select, this.state.checked && styles.checked]} onPress={this.select} /> } }
2.Header.js
/** * 头部组件 * 引用组件 矢量图标 npm install react-native-vector-icons@3.x --save * 原生的组件需要link react-native link */ import React, { Component } from 'react'; import { AppState, StyleSheet, View, Text, TouchableOpacity } from 'react-native'; // 引入矢量图标组件 import Icon from 'react-native-vector-icons/FontAwesome'; const styles = StyleSheet.create({ root: { flexDirection: 'row', height: 44, backgroundColor: '#F5F5F5', justifyContent: 'space-between', paddingHorizontal: 20, alignItems: 'center', }, text: { textAlign: 'center', fontSize: 18, }, back: { fontSize: 20, color: '#900', }, right: { fontSize: 20, color: 'transparent', } }); export default class Header extends Component { goBack = () => { // 通过解钩 获取传递的navigator const { navigator } = this.props; navigator.pop(); }; render() { return ( <View style={styles.root}> <TouchableOpacity onPress={this.goBack}> {/*引入Icon组件*/} <Icon name="rocket" style={styles.back} /> </TouchableOpacity> <Text style={styles.text}>购物车</Text> <TouchableOpacity> {/*引入Icon组件*/} <Icon name="rocket" style={styles.right} /> </TouchableOpacity> </View> ); } }
3.ItemList.js
/** * 列表组件 */ import React, { Component } from 'react'; import { StyleSheet, ScrollView } from 'react-native'; // 引入Item组件 import Item from './Item'; const styles = StyleSheet.create({ root: { flex: 1, } }); // 引入购物车数据 import cartData from '../logics/CartData'; export default class ItemList extends Component { render() { // 取出父组件传递的参数 const { cartData } = this.props; // 动态创建组件 return ( <ScrollView style={styles.root}> { cartData.map((data,index) => { return <Item key={data.id} index={index} data={data} cartData={cartData} /> }) } </ScrollView> ); } }
4.Item.js
/** * 列表子组件 */ import React, { Component } from 'react'; import { StyleSheet, View, Image, TouchableOpacity, Text } from 'react-native'; // 引入observer import { observer } from 'mobx-react/native'; // 引入Circle组件 import Circle from './Circle'; const styles = StyleSheet.create({ root: { flex: 1, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 10, height: 100, }, img: { 90, height: 90, }, content: { // }, price: { // }, name: { fontSize: 16, }, priceAndControls: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }, button: { padding: 10, justifyContent: 'center', borderWidth: StyleSheet.hairlineWidth, borderColor: '#000', }, buttonText: { // } }); // 监听observer @observer export default class Item extends Component { check = (checked) => { const { index, cartData } = this.props; cartData.check(checked, index); }; minus = () => { const { index, data: { count }, cartData } = this.props; // 安全检查 if(count > 1){ cartData.minus(index); } }; plus = () => { const { index, data: { count }, cartData } = this.props; cartData.plus(index); }; render() { // 取出父组件传递的值 props上的数据只可读不可写(不可更改) const { index, data: {id,name,count,img,checked} } = this.props; return ( <View style={styles.root}> {/*勾选框*/} <Circle onPress={this.check} /> {/*图片*/} <Image style={styles.img} source={{uri: img}} /> {/*描述*/} <View style={styles.content}> <Text style={styles.name}>{name}</Text> <View style={styles.priceAndControls}> <Text style={styles.price}>¥{price.toFixed(2)}</Text> <TouchableOpacity style={styles.button} onPress={this.minus} > <Text style={styles.buttonText}>-</Text> </TouchableOpacity> <Text>{count}</Text> <TouchableOpacity style={styles.button} onPress={this.plus} > <Text style={styles.buttonText}>+</Text> </TouchableOpacity> </View> </View> </View> ); } }
5.Footer.js
/** * 底部组件 */ import React, { Component } from 'react'; import { AppState, StyleSheet, View, Text, TouchableOpacity } from 'react-native'; // 引入矢量图标组件 import Icon from 'react-native-vector-icons/FontAwesome'; const styles = StyleSheet.create({ root: { position: 'absolute', bottom: 0, left: 0, // 设置left、right为0,可实现100%的效果 right: 0, justifyContent: 'center', flexDirection: 'row', height: 44, backgroundColor: '#F5F5F5', justifyContent: 'space-between', borderTopWidth: StyleSheet.hairlineWidth, // 解决1px问题,专门用来设置边界值 alignItems: 'center', // 元素居中 }, selectWrapper: { flexDirection: 'row', // 设置主轴为横向 alignItems: 'center', marginLeft: 20, }, checked: { backgroundColor: '#f23030', }, selectText: { marginLeft: 5, }, checkout: { backgroundColor: '#f23030', paddingHorizontal: 20, height: 50, justifyContent: 'center', }, checkoutText: { fontSize: 18, color: '#fff', } }); // 引入observer组件 import { observer } from 'mobx-react/native'; // 引入勾选框组件 Circle import Circle from '../components/Circle'; @observer export default class Footer extends Component { // 构造器 状态 // constructor(props) { // super(props); // this.state = { // // // } // } goBack = () => { // 通过解钩 获取传递的navigator const { navigator } = this.props; navigator.pop(); }; selectAll = (checked) => { // 获取子组件返回值 alert(checked); }; render() { const { cartData } = this.props; return ( <View style={styles.root}> <View style={styles.selectWrapper}> <Circle onPress={this.selectAll} /> <Text style={styles.selectText}>全选</Text> </View> <Text>总计:¥{cartData.sum.get()}</Text> <TouchableOpacity // 状态不同显示的样式不同 style={styles.checkout} onPress={this.checkout} > <Text style={styles.checkoutText}>去结算({cartData.count.get()})</Text> </TouchableOpacity> </View> ); } }
六、数据
CartData.js
/** * 通过observer 将数据变成可变的数据(可读写) */ import { observable, computed } from 'mobx'; /** * 独立于组件的变量,用于存储组件状态 */ const cartData = observable([ { id: '928128', name: '飞利浦(PHILIPS)电吹风机 HP8230 家用大功率恒温护发冷热风', price: 620000, count: 1, img: 'http://img10.360buyimg.com/n7/g14/M06/04/15/rBEhV1HcwMEIAAAAAACwKUjHr8IAAA6egEjTU4AALBB222.jpg!q70.jpg.webp', checked: false, }, { id: '3926802', name: '和情(LOTUS)缤咖时焦糖饼干250g*2袋装', price: 6180000, count: 1, img: 'http://img10.360buyimg.com/n7/s176x176_jfs/t3715/282/1024231787/79825/c65fba1d/581aeeb3N5802976f.jpg!q70.jpg.webp', checked: false }, ]); // 封装对数据操作的方法 cartData.minus = (index) => { cartData[index].count -= 1; }; cartData.plus = (index) => { cartData[index].count += 1; }; cartData.check = (checked, index) => { cartData[index].checked = checked; }; // 在现有的数据上计算结束后得到的结果 cartData.count = computed(() => { // reduce 类似击鼓传花 层层叠加 return cartData.reduce((a,b) => { if(b.checked){ return a + b.count; }else{ return a; } // return a + b.checked && b.count; },0); }); cartData.sum = computed(() => { // reduce 类似击鼓传花 层层叠加 return cartData.reduce((a,b) => { if(b.checked){ return a + b.count * b.price; }else{ return a; } // return a + b.checked && (b.price * b.count); },0); }); export default cartData;
.