php react-native,React-Native+Mobx实现商城APP

这次给大家带来React-Native+Mobx实现商城APP,React-Native+Mobx实现商城APP的注意事项有哪些,下面就是实战案例,一起来看一下。

最近一直在学习微信小程序,在学习过程中,看到了 wxapp-mall

这个微信小程序的项目,觉得很不错,UI挺小清新的,便clone下来研究研究,在看源码过程中,发现并不复杂,用不多的代码来实现丰富的功能确实令我十分惊喜,于是,我就想,如果用react-native来做一个类似这种小项目难不难呢,何况,写一套代码还能同时跑android和ios(小程序也是。。。),要不写一个来玩玩?有了这个想法,我便直接

react-native init 一个project来写一下吧(๑•̀ㅂ•́)و✧

技术框架以及组件react "16.0.0"

react-native "0.51.0"

mobx: "3.4.1"

mobx-react: "4.3.5"

react-navigation: "1.0.0-beta.21"

react-native-scrollable-tab-view: "0.8.0"

react-native-easy-toast: "1.0.9"

react-native-loading-spinner-overlay: "0.5.2"

为什么要用Mobx?

Mobx是可扩展的状态管理工具,比react-redux要简单,上手也比较快。在这个小项目中,因为没有后台服务接口,用的都是本地的假数据,为了模拟实现

浏览商品 =>加入购物车=>结账=>清空购物车=>还原商品原始状态

这么一个流程,便用Mobx来管理所有的数据以及商品的状态(有没有选中,有没有加入购物车),这样,所有的页面都可以共享数据以及改变商品的状态,页面之间的数据和商品状态都是同步更新的。具体用Mobx怎么来实现这流程,在下面会分享使用感受和遇到的一些小坑。

开始

先react-native

init一个project,然后用yarn或者npm装好所有的依赖和组件。因为使用Mobx会用到ES7中装饰器,所以还要安装

babel-plugin-transform-decorators-legacy 这个插件,然后在.babelrc文件下添加一下内容即可。{

"presets": ["react-native"],

"plugins": ["transform-decorators-legacy"]

}

项目结构|-- android

|-- ios

|-- node_modules

|-- src

|-- common // 公用组件

|-- img // 静态图片

|-- mobx // mobx store

|-- newGoods.js // 首页新品数据

|-- cartGoods.js // 购物车数据

|-- categoryGoods.js // 分类页数据

|-- store.js // store仓库,管理数据状态

|-- scene

|-- Cart // 购物车页面

|-- Category // 分类页

|-- Home // 首页

|-- ItemDetail // 商品信息页

|-- Mine // 我的页面

|-- Root.js // root.js主要内容是配置react-navigation(导航器)

|-- index.js // 主入口

在Root.js文件中,有关react-navigation的配置和使用方法可以参考下官方文档和这篇博客,里面都写得十分详细,有关react-navigation的疑问我都在这2篇文章中找到答案,在这里相关react-navigation配置,使用方法和项目里面页面布局,组件写法,在这里不打算细说,因为都比较简单,更多的是讨论Mobx实现功能的一些逻辑和方法,

screen 文件夹下的组件都写有注释的(°ー°〃)

主要还是来聊聊Mobx吧

1.数据存储和获取

这些都是用假数据来模拟实现的,在最开始,先写好假数据的数据结构,例如:"data":

[{

"name": '那么大西瓜',

"price": '2.0',

"image": require('../img/a11.png'),

"count": 0,

"isSelected": true

},...]

在 Mobx 文件夹下的 store.js, 在这里主要是存储和管理app用到的所有商品的数据,将 逻辑 和 状态 从组件中移至一个独立的,可测试的单元,这个单元在每个页面下都可以用到import { observable, computed, action } from 'mobx'

import cartGoods from './cartGoods'

import newGoods from './newGoods'import categoryGoods from './catetgoryGoods'

/**

* 根store

* @class RootStore

* CartStore 为购物车页面的数据

* NewGoodsStore 为首页的数据

* categoryGoodsStore 为分类页的数据

*/

class RootStore {

constructor() {

this.CartStore = new CartStore(cartGoods,this)

this.NewGoodsStore = new NewGoodsStore(newGoods,this)

this.categoryGoodsStore = new categoryGoodsStore(categoryGoods,this)

}}

Class CartStore{

@observable allDatas = {}

constructor(data,rootStore) {

this.allDatas = data

this.rootStore = rootStore

}

}

Class NewGoodsStore{

...跟上面一样

}

Class categoryGoodsStore{

...跟上面一样

}

// 返回RootStore实例

export default new RootStore()

这里用了 RootStore 来实例化所有了stores(购物车,首页,分类页分别拥有各自的store),

这样,可以通过RootStore 来管理和操作stores,从而实现它们之间的相互通信,共享引用。

其次,存储数据用了Mobx的@observable方法,就是把数据成为观察者,当用户操作视图,导致数据发生变化时,配合react-mobx提供的@observer可以自动更新视图,非常方便。

此外,为了把Mobx 的Rootstore注入到react-native的组件中,要通过 mobx-react 提供的 Provider 实现,在 Root.js 下,我是这么写的:// 全局注册并注入mobx的Rootstore实例,首页新品,分类页,商品详情页,购物车页面都要用到store

import {Provider} from 'mobx-react'

// 获取store实例

import store from './mobx/store'

const Navigation = () => {

return (

)}

把Rootstore实例注入到组件树中后,那么,是不是在组件中直接使用 this.props.rootStore 就可以取到了呢?

‘'不是的”,我们还需要在要用到Rootstore的组件里,要加点小玩意,在 HomeScreen.js (首页)中这么写:import { inject, observer } from 'mobx-react'

@inject('rootStore') // 缓存rootStore,也就是在Root.js注入的

@observerexport default class HomeScreen extends Component {

......

}

加上了 @inject('rootStore') ,我们就可以愉快地使用 this.props.rootStore

来拿到我们想要的数据啦^_^ ,同样,在商品信息,分类页,购物车页面js下,也需要使用 @inject('rootStore')

来实现数据的获取,然后再一步步地把数据传到它们的子组件中。

2. 加入购物车的实现

在首页和分类页中,都可以点击跳转到商品信息页,然后再加入到购物车里

be6b32f1f1.gif

实现方法 :

在itemDetail.js下,也就是商品信息页面下,加入购物车的逻辑是这样子的:addCart(value) {

if(this.state.num == 0) {

this.refs.toast.show('添加数量不能为0哦~')

return;

}

// 加入购物车页面的列表上

// 点一次,购物车数据同步刷新

this.updateCartScreen(value)

this.refs.toast.show('添加成功^_^请前往购物车页面查看')

}

// 同步更新购物车页面的数据

updateCartScreen (value) {

let name = this.props.navigation.state.params.value.name;

// 判断购物车页面是否存在同样名字的物品

let index;

if(this.props.rootStore.CartStore)

index = this.props.rootStore.CartStore.allDatas.data.findIndex(e => (e.name === name))

// 不存在

if(index == -1) {

this.props.rootStore.CartStore.allDatas.data.push(value)

// 加入CartStore里

// 并让购物车icon更新

let length = this.props.rootStore.CartStore.allDatas.data.length

this.props.rootStore.CartStore.allDatas.data[length - 1].count += this.state.num}

else {

// 增加对应name的count

this.props.rootStore.CartStore.allDatas.data[index].count += this.state.num

}}

简单的说,先获取水果的名称name,然后再去判断Mobx的CartStore里面是否存在同样的名称的水果,如果有就增加对应name的数量count,如果没有,就往CartStore中增加数据,切换到购物车页面时,视图会同步刷新,看到已加入购物车的水果。

3.改变商品状态同步更新视图

当用户在购物车页面操作商品状态时,数据改变时,视图会跟着同步刷新。

例如,商品的增加数量,减少数据,选中状态,商品全选和商品删除,总价格都会随着商品的数量变化而变化。

图又来了~~

实现上面的功能,主要用到了Mobx提供的action方法,action是用来修改状态的,也就是用action来修改商品的各种状态(数量,选中状态...),这些action,我是写在 store.js 的 CartStore类 中的,下面贴出代码// 购物车store

class CartStore {

@observable allDatas = {}

constructor(data,rootStore) {

this.allDatas = data

this.rootStore = rootStore

}

//加

@action

add(money) {

this.allDatas.totalMoney += money

}

// 减

@action

reduce(money) {

this.allDatas.totalMoney -= money

}

// checkbox true

@action

checkTrue(money) {

this.allDatas.totalMoney += money

}

// checkbox false

@action

checkFalse(money) {

if(this.allDatas.totalMoney <=0 )

return

this.allDatas.totalMoney -= money

}

// 全选

@action

allSelect() {

if(this.allDatas.isAllSelected) {

// 重置totalMoney

this.allDatas.totalMoney = 0

this.allDatas.data.forEach(e=> {

this.allDatas.totalMoney += e.count * e.price})}

else {

this.allDatas.totalMoney = 0

}}

// check全选

@action

check() {

// 所有checkbox为true时全选才为true

let allTrue = this.allDatas.data.every(v => ( v.isSelected === true ))

if(allTrue) {

this.allDatas.isAllSelected = true

}else {

this.allDatas.isAllSelected = false

}}

// 删

@action

delect(name) {

this.allDatas.data = this.allDatas.data.filter (e => (e.name !== name ))

}

// 总价格

@computed get totalMoney() {

let money = 0;

let arr = this.allDatas.data.filter(e => (e.isSelected === true))

arr.forEach(e=> (money += e.price * e.count))

return money

}}

所有修改商品状态的逻辑都在上面代码里面,其中,totalMoney是用了Mobx的@computed方法,totalMoney是依赖于CartStore的data数据,也就是商品数据,但data的值发生改变时,它会重新计算返回。如果了解vue的话,这个就相当于vue的计算属性。

4.结算商品

商品结算和清空购物车的逻辑都写在 CartCheckOut.js 里面,实现过程很简单,贴上代码吧:// 付款

pay() {

Alert.alert('您好',`总计:¥ ${this.props.mobx.CartStore.totalMoney}`,

{text: '确认支付', onPress: () => this.clear()},

{text: '下次再买', onPress: () => null}],{ cancelable: false })}

// 清空购物车

clear() {

this.setState({visible: !this.state.visible})

setTimeout(()=>{

this.setState({ loadText: '支付成功!欢迎下次光临!' })

setTimeout(()=> { this.setState({ visible: false },

()=>{ this.props.mobx.CartStore.allDatas.data = []

// 把所有商品count都变为0

this.props.mobx.NewGoodsStore.allDatas.data.forEach(e=> e.count = 0)

this.props.mobx.categoryGoodsStore.allDatas.data.forEach( e => {

e.detail.forEach(value => { value.count = 0 })

})

})},1500)},2000)}

这里主要用了setTimeout和一些方法来模拟实现 支付中 => 支付完成 => 清空购物车 => 还原商品状态。

好了,这个流程就搞定了,哈哈。

5.遇到的小坑

1.我写了一个数组的乱序方法,里面有用到 Array.isArray()

这个方法来判断是否为数组,但是,我用这个乱序函数时,想用来搞乱store里面的数组时,发现一直没有执行,觉得很奇怪。然后我直接用

Array.isArray()

这个方法来判断store里面的数组,返回的一直都是false。。。于是我就懵了。。。后来,我去看了Mobx官方文档,终于找到了答案。原来,store里面存放的数组,并不是真正的数组,而是

obverableArray ,如果要让 Array.isArray() 判断为true,就要在取到store的数组时,加个. slice()

方法,或者 Array.from() 都可以。

2.同样,也是obverableArray的问题。在购物车页面时,我用了FlatList来渲染购物车的item,起初,当我增加商品到购物车,发现购物车页面并没有刷新。有了上面的踩坑经验,我认为是obverableArray引起的,因为FlatList的data接收的是real

Array,于是,我用这样的方法:@computed get dataSource() {

return this.props.rootStore.CartStore.allDatas.data.slice();

}

...

于是,购物车视图就可以自动地刷新了,在官方文档上也有写到。

3.还有一个就是自己粗心造成的。我写完这个项目后,和朋友出去玩时,顺便发给朋友看看,他在删除商品时发现,从上往下删删不了,从下往上删就可以。后来我用模拟器测试也是如此,于是就去看看删除商品的逻辑,发现没有问题,再去看store的数据,发现也是可以同步更新的,只是视图没有更新,很神奇,于是我又在FlatList去找原因,终于,原因找到了,主要是在keyExtractor里面,用index是不可以的,要用name来作为key,因为我删除商品方法其实是根据name来删的,而不是index,所以用index来作为FlatList的Item的key时是会出现bug的。_keyExtractor = (item,index)=> {

// 千万别用index,不然在删购物车数据时,如果从第一个item开始删会产生节点渲染错乱的bug

return item.name

}

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值