使用 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,
width: 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: {
width: 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,可实现width: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;
.