勤学苦练项目,Mobx做1个娇小玲珑水果商号APP

  前几日到底把商品页和购物车作用弄出来了,在这么些开辟进程中相见有个别小坑,比如购物车和商品页是分开七个零件的,未有利用到vue的双向数据绑定的特色,导致在操作加减商品数量时几个零件的多少尚未联手,后来自家就重写了1回,好好的行使了vuex的状态保持,这么些东西确实很好用。先秀1段小编写的vuex代码吧!

  今日终于把商品页和购物车作用弄出来了,在那么些开采进程中遭受某个小坑,比如购物车和商品页是分手八个零件的,未有运用到vue的双向数据绑定的特征,导致在操作加减商品数量时三个零件的数目未有一同,后来自家就重写了一回,好好的使用了vuex的动静保持,那么些东西确实很好用。先秀一段作者写的vuex代码吧!

前言

澳门葡京 1

 1 //状态管理
 2 export default (Vuex) => {
 3     return new Vuex.Store({
 4         state: {
 5             totalMoney: 0, //已选购商品总价格
 6             productArray: [] //已选购商品数组
 7         },
 8         mutations: {
 9             setTotalMoney(state, num) { //设置商品总价格
10                 state.totalMoney = num;
11             },
12             mathTotalMoney(state) { //计算已选购商品总价格
13                 let total = 0;
14                 for (let i = 0; i < state.productArray.length; i++) {
15                     let item = state.productArray[i];
16                     total += (item.count * item.price);
17                 }
18                 state.totalMoney = total;
19             },
20             setProductArray(state, obj) { //商品放入或拿出购物车
21                 let index = -1;
22                 for (let i = 0; i < state.productArray.length; i++) {
23                     var item = state.productArray[i];
24                     if (obj.id == item.id) {
25                         index = i;
26                         break;
27                     }
28                 }
29                 if (index >= 0) {
30                     if (obj.count <= 0) {
31                         state.productArray.splice(index, 1);
32                     } else {
33                         state.productArray[index] = obj;
34                     }
35                 } else {
36                     state.productArray.push(obj);
37                 }
38             },
39             clearProduct(state) { //清空购物车
40                 state.productArray = [];
41             }
42         },
43         getters: {
44             getTotalMoney(state) { //获取商品总价格
45                 return state.totalMoney;
46             },
47             getProductArray(state) { //获取已选购商品
48                 return state.productArray;
49             },
50             getProductById: (state, getters) => (id) => { //根据ID获取已选商品
51                 for (let i = 0; i < state.productArray.length; i++) {
52                     var item = state.productArray[i];
53                     if (item.id == id) {
54                         return item;
55                     }
56                 }
57                 return false;
58             }
59         }
60     });
61 }
 1 //状态管理
 2 export default (Vuex) => {
 3     return new Vuex.Store({
 4         state: {
 5             totalMoney: 0, //已选购商品总价格
 6             productArray: [] //已选购商品数组
 7         },
 8         mutations: {
 9             setTotalMoney(state, num) { //设置商品总价格
10                 state.totalMoney = num;
11             },
12             mathTotalMoney(state) { //计算已选购商品总价格
13                 let total = 0;
14                 for (let i = 0; i < state.productArray.length; i++) {
15                     let item = state.productArray[i];
16                     total += (item.count * item.price);
17                 }
18                 state.totalMoney = total;
19             },
20             setProductArray(state, obj) { //商品放入或拿出购物车
21                 let index = -1;
22                 for (let i = 0; i < state.productArray.length; i++) {
23                     var item = state.productArray[i];
24                     if (obj.id == item.id) {
25                         index = i;
26                         break;
27                     }
28                 }
29                 if (index >= 0) {
30                     if (obj.count <= 0) {
31                         state.productArray.splice(index, 1);
32                     } else {
33                         state.productArray[index] = obj;
34                     }
35                 } else {
36                     state.productArray.push(obj);
37                 }
38             },
39             clearProduct(state) { //清空购物车
40                 state.productArray = [];
41             }
42         },
43         getters: {
44             getTotalMoney(state) { //获取商品总价格
45                 return state.totalMoney;
46             },
47             getProductArray(state) { //获取已选购商品
48                 return state.productArray;
49             },
50             getProductById: (state, getters) => (id) => { //根据ID获取已选商品
51                 for (let i = 0; i < state.productArray.length; i++) {
52                     var item = state.productArray[i];
53                     if (item.id == id) {
54                         return item;
55                     }
56                 }
57                 return false;
58             }
59         }
60     });
61 }

日前一贯在上学微信小程序,在上学进度中,看到了 wxapp-mall
这几个微信小程序的品类,以为很精确,UI挺小清新的,便clone下来研商商量,在看源码进程中,开掘并不复杂,用不多的代码来完结增进的意义确实令本人这么些欣喜,于是,小编就想,如若用react-native来做一个近似那种小品种难简单呢,何况,写一套代码还可以同时跑android和ios(小程序也是。。。),要不写三个来娱乐?有了这几个想法,小编便一向react-native init 3个project来写一下呢(๑•̀ㅂ•́)و✧

显示器快速照相 2018-0二-2捌 清晨拾.27.3伍.png

  但是自身总认为,小编这么的用法有点不太对的。贴个代码,希望有权威指引下,我这么使用vuex可取不。

  可是作者总认为,作者如此的用法有点不太对的。贴个代码,希望有权威辅导下,笔者如此使用vuex可取不。

先来张动图,dengdengdeng~~

进度分析

一.先是购物车弹窗是三个组件,因为会油然则生在不一样的页面中。
二.因为众多组件会用到购物车数据,所以集结置于vuex中。

 1 import Vue from 'vue';
 2 import App from './App';
 3 import router from './router';
 4 import VueResource from 'vue-resource';
 5 import Vuex from 'vuex';
 6 import vuex_store from './store';
 7 
 8 Vue.use(VueResource);
 9 Vue.use(Vuex);
10 
11 new Vue({
12     el: '#app',
13     router,
14     template: '<App/>',
15     components: { App },
16     store: vuex_store(Vuex)
17 });
 1 import Vue from 'vue';
 2 import App from './App';
 3 import router from './router';
 4 import VueResource from 'vue-resource';
 5 import Vuex from 'vuex';
 6 import vuex_store from './store';
 7 
 8 Vue.use(VueResource);
 9 Vue.use(Vuex);
10 
11 new Vue({
12     el: '#app',
13     router,
14     template: '<App/>',
15     components: { App },
16     store: vuex_store(Vuex)
17 });

澳门葡京 2 

得以实现步骤解析

  上边代码正是进口文件,小编前些天 vuex
对象再传出自身要好写的不行store模块中。接着继续说本身的商品页和购物车呢,贴个动图给大看看效果如何。

  上边代码便是进口文件,小编后日 vuex
对象再盛传本人自个儿写的要命store模块中。接着继续说自家的商品页和购物车吧,贴个动图给大看看效果如何。

技能框架以及组件

1、到场购物车

  1. 将购物车多少统一放在vuex中:
    state里停放2个数组:carPanelData,里面放置购物车数量。

// 购物车商品数据
state: {
    carPanelData: []
}
  1. 往购物车里push数据:在mutation里面改动state数据。
    思路:首先必要将点击参与购物车的每一条数据加2天性质:count,计数,然后将那条点击的货物数量push到state中,当然,首先是内需先用商品ID和state里的商品ID比对,假诺未有就push,即使有了,就计数。
    以下是代码思路

mutations:
    addCarPanelData(state,加入购物车的数据data){
        //循环carPanelData购物车数据
        //如果商品ID存在(购物车的id和传进来的ID比对),就设置count++
        //设置开关false
===================================================================
        **注意** : 如果上面的条件成立,以下是不执行的,所以可以设置一个开关bOff
        //如果开关值为true
        //否则就是商品ID不存在,设置一个新的变量goodsData = 传进来的data;
        //Vue.set(goodsData,'count',1):为这个变量设置count属性,值为1;
        //将这个goodsData,push到carPanelData中;              
}
--------------------------------------------------------------
 mutations: {
    addCarPanelData (state,data) {
      let bOff = true
      state.carPanelData.forEach((goods) => {
        if (goods.sku_id === data.sku_id) {
          goods.count++
          bOff = false
        }
      })
      if (bOff) {
        let goodsData = data
        Vue.set(goodsData,'count',1)
        state.carPanelData.push(goodsData)
      }
      console.log(state.carPanelData)
    }
  }
  1. 在商品页选拔了货色,点击参加购物车按键:那时,将那条数据传给vuex,记录到state中(mutation里面已经对该逻辑进行了拍卖)
    要点 : 记住vuex的怀想,要想改变state,必须交给mutation。

 methods:{
       addCarPanelHandel(data){
        //改变state,必须提交mutation,并将此条数据传给vuex
          this.$store.commit('addCarPanelData',data)
        }
}
  1. 那会儿早已主导完结了购物车的逻辑,上面,我们把购物车单独出来做成组件:car-panel。
    本条时候就足以把相关数据绑定在购物车了。
    要点勤学苦练项目,Mobx做1个娇小玲珑水果商号APP。 :如何获得在购物车组件内得到vuex数据?
    用computed即可。

//相应的绑定代码=示例
<a href="#/item/100027401">{{item.title}})</a>
//获取vuex数据
computed : {
        carPanelData(){
            return this.$store.state.carPanelData
        },
        count(){
            return this.$store.getters.totleCount
        },
        totle(){
             return this.$store.getters.totlePrice
        }
}
  1. 最终,对购物车中的商品数量和商品总价总结。
    在vue中,我们供给对变量进行更为管理,能够投身computed里,不提出坐落模板中,一样,vuex中,state中的状态尽管必要越来越管理,大家得以放入getters.

getters:{
    // 购物车商品数量计算
    totleCount (state) {
      let count = 0
      state.carPanelData.forEach((goods) => {
        count += goods.count
      })
      return count
    },
    // 总价格
    totlePrice (state) {
      let price = 0
      state.carPanelData.forEach((goods) => {
        price += goods.price * goods.count
      })
      return price
    }
  }

============================================================

澳门葡京 3

澳门葡京 4

  • 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”

从那之后,已经完成了加购物车,并且总计数据和金额。

  商品也和购物车效用,一时半刻就这一个了。入眼依旧在布局上,js上的逻辑都轻松。能够上本人的github获取源码看看咯。

  商品也和购物车功效,临时就这么些了。入眼照旧在布局上,js上的逻辑都轻便。可以上自己的github获取源码看看咯。

缘何要用Mobx?

二、购物车删除

一同头本人的思绪是
1、删除数据一定是要转移state,改变state肯定是索要付出mutation,所以删除的相干逻辑格局应该写在mutation;
二、当时本身的主题素材是如何精晓删除的是哪一条数据?
经过学习,弄驾驭了,今后此类须求,都和参预购物和思路是平等的,都以由此对照删除的当前的ID和多少里的有所ID实行比对,就精通是删除具体哪条数据了。
三、那么本人急需记住,当前挑选的是哪条数据,都以因而在剔除的点击方法对应的风云里,参数中传送当前数码(商品ID)就可以。那是三个思路,必要牢记
切实贯彻步骤总括
点击购物车页面包车型地铁去除商品开关,绑定二个刨除方法,参数传入当前被点击的货色ID,在那些点子里调用mutation里面包车型大巴去除商品方法:

  1. 先是须要循环state的购物车多少;
  2. 比对每壹项的货物ID是不是和脚下盛传的ID同样,假若是均等的那么就return,不再继续循环了;
  3. 在state的购物车多少里删除那项ID同样的数据。

//mutation 
delCarPanelData (state,id) {
      state.carPanelData.forEach((goods,index) => {
        if (goods.sku_id === id) {
          state.carPanelData.splice(index,1)
          return
        }
      })
}
//购物车组件中
methods: {
        delCarPanelHandel(id){
            this.$store.commit('delCarPanelData',id)
        }
}
<div class="del-btn"  @click="delCarPanelHandel(item.sku_id)"> 删除 </div>

  源码地址:

  源码地址:

Mobx是可扩大的景况管理工具,比react-redux要简明,上手也正如快。在那几个小项目中,因为尚未后台服务接口,用的都以地方的假数据,为了模仿完结浏览商品 =>参与购物车=>买下账单=>清空购物车=>还原商品原始状态
这么八个流水生产线,便用Mobx来保管全数的数码以及商品的动静(有未有入选,有未有投入购物车),这样,全部的页面都足以共享数据以及改动商品的景观,页面之间的数据和商品状态都以一齐更新的。具体用Mobx怎么来贯彻那流程,在底下会享受应用感受和蒙受的部分小坑。

三、购物车商品数量限制

思路 : 那类显示隐藏的案例,都以安装变量属性的ture/false
先是是有二个弹窗组件,当商品数量高出最大值得时候,这几个组件必要弹出。
数据中一度有了最大值 : limit_num。
在state中定义1个变量:maxOff :false
,暗中认可不出示,当购物车中物品扩大的时候,比对当前商品的数码是不是曾经超(Jing Chao)过了limit_num,假若是,就让这一个弹窗出来,约等于在mutation中安装该属性为true。

  • 货色数量超过后展现弹窗

 // 加入购物车
    addCarPanelData (state,data) {
      state.carPanelData.forEach((goods) => {
        if (goods.sku_id === data.sku_id) {
          goods.count++
          bOff = false
          //比较当前商品的数量和数据中的商品最大购买数量
          if (goods.count > goods.limit_num) {
            goods.count--
            state.maxOff = true
          }
        }
      })
 }
//组件中
<div id="prompt" v-if="maxOff">

 computed: {
        maxOff(){
            return this.$store.state.maxOff
        }
  }
  • 关门弹窗

//mutations
   closePrompt (state) {
     state.maxOff = false
   }
//组件中
  methods : {
       closePrompt(){
           this.$store.commit('closePrompt')
       }
   }

开始

4、购物车展现隐藏

思路: 同样的,那类呈现隐藏,需求安装二个开关,去切换按钮就可以。

 state: {
    carPanelData: [],
    maxOff : false, // 弹窗开关
    carShow : false, // 购物车开关
    carTimer : null // 购物车定时器
  },
 // 购物车显示
    showCar (state) {
      clearTimeout(state.carTimer)
      state.carShow = true
    },
    // 购物车隐藏
    hideCar (state) {
      state.carTimer = setTimeout(() => {
        state.carShow = false
      },1000)
    }
===================
//组件中
 methods: {
        // 显示购物车
        showCar(){
           this.$store.commit('showCar')
        },
        // 隐藏购物车
        hideCar(){
          this.$store.commit('hideCar')
        }
 }

先react-native
init1个project,然后用yarn也许npm装好全部的依赖和组件。因为使用Mobx会用到ES7中装饰器,所以还要设置
babel-plugin-transform-decorators-legacy
这么些插件,然后在.babelrc文件下增多一下内容就可以。

4、购物车小球效果

思路:用的vue的transtion钩子函数,原理便是先把小球写死到购物车,点击的时候须臾间移入到须要的任务,然后做贰个衔接动画就可以,参与贝塞尔曲线。

state: {
    ball: { // 购物车小球
      show: false,
      el: null, // 点击的是哪个购物车按钮
      img: ''
}
mutations: {
    // 加入购物车
    addCarPanelData (state,data) {
      // 加上这个条件,确保小球飞完再添加
      if (!state.ball.show) {
        // 显示购物车
        state.carShow = true
        let bOff = true
        state.carPanelData.forEach((goods) => {
          // 比对ID,相同就说明购物车已存在此商品,数量增加
          if (goods.sku_id === data.sku_id) {
            goods.count++
            bOff = false
            if (goods.count > goods.limit_num) {
              goods.count--
              state.maxOff = true
              return
            }
            // 加入购物车,小球显示
            state.ball.show = true
            state.ball.img = data.ali_image
            // 通过event对象获取到当前点击的按钮
            state.ball.el = event.path[0]
          }
        })
        // 商品不存在,就往数组里新增商品数据
        if (bOff) {
          let goodsData = data
          Vue.set(goodsData,'count',1)
          state.carPanelData.push(goodsData)
          // 加入购物车,小球显示
          state.ball.show = true
          state.ball.img = data.ali_image
          // 通过event对象获取到当前点击的按钮
          state.ball.el = event.path[0]
        }
        console.log(event)
      }
    }
============
//组件内
 // 小球进入前,初始化
      beforeEnter(el){
        // 获取按钮的位置
        let rect = this.ball.el.getBoundingClientRect()
        // 获取购物车
        let rectEl = document.getElementsByClassName('ball-rect')[0].getBoundingClientRect()
        // 获取小球
        let ball = document.getElementsByClassName('mask-item')[0]
        //计算按钮和购物车的差值 : 购物车的中心点到左侧的距离 - 按钮中心点到左侧的距离
        let x = (rectEl.left + 16) - (rect.left + rect.width/2)
        let y = rect.top + rect.height/2 - rectEl.top + 5 - 16
        //计算小球和包着小球的父级的位置
        ball.style.transform = 'translate3d(-'+x+'px,0,0)'
        el.style.transform = 'translate3d(0,'+y+'px,0)'

        ball.src = this.ball.img
        console.log(this.ball.img)
      },
      //开始运动
      enter (el){
        let a = el.offsetHeight
        // 获取小球
        let ball = document.getElementsByClassName('mask-item')[0]
        el.a = a //避免变量没有使用,eslint报错
        el.style.transform = "translate3d(0,0,0)"
        ball.style.transform = "translate3d(0,0,0)"
      },
      // 结束,让小球隐藏
      afterEnter (el){
        this.ball.show = false
      }
    }
===========
    <transition 
      name="ball" 
      v-on:before-enter="beforeEnter" 
      v-on:enter="enter" 
      v-on:after-enter="afterEnter" 
      v-bind:css="true"
    >
      <div class="addcart-mask" v-show="ball.show">
        <img class="mask-item" src="" alt="澳门葡京 5">
      </div>
    </transition>
 ==========
 .ball-enter-active{
    transition: 1s cubic-bezier(.18,1,.94,1)
  }
  .ball-enter-active .mask-item{
    transition: 1s cubic-bezier(0,0,0,0)
  }
{ 
 "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吧

先来看看用Mobx达成的切实可行流程,看下边包车型客车动图(⊙﹏⊙)

ps: 大概图片太大,加载有点慢,请稍等……

澳门葡京 6 

一.多少存储和收获

那个都以用假数据来效仿完毕的,在最开首,先写好假数据的数据结构,比如:

"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 ( 
 <Provider rootStore={store}> 
 <Navigator/> 
 </Provider> 
)}

把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’)
来落成数据的获得,然后再一步步地把数量传到它们的子组件中。

二. 参预购物车的落到实处

在首页和分类页中,都足以点击跳转到商品音讯页,然后再投入到购物车里

澳门葡京 7 

贯彻形式 :

在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中扩展数量,切换来购物车页面时,视图会同步刷新,看到已进入购物车的果品。

三.改观商品状态同步创新视图

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

譬如说,商品的扩展数据,减弱数量,选中状态,商品全选和货色删除,总标价都会趁着商品的数码变化而变化。

澳门葡京 8 

图又来了~~

落到实处地方的功能,首要运用了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的测算属性。

四.买单商品

货品买下账单和清空购物车的逻辑都写在 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和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();
}
...
<FlatList data={this.dataSource} .../>

于是,购物车视图就足以自行地刷新了,在合法文书档案上也有写到。

三.还有三个便是上下一心疏忽形成的。笔者写完那些项目后,和爱人出去玩时,顺便发给朋友看看,他在剔除商品时意识,从上往下删删不了,从下往上删就能够。后来本人用模拟器测试也是那般,于是就去探望删除商品的逻辑,开掘未有失水准,再去看store的数额,开掘也是足以共同更新的,只是视图未有更新,极美妙,于是小编又在FlatList去找原因,终于,原因找到了,首借使在keyExtractor里面,用index是不得以的,要用name来作为key,因为本身删除商品方法其实是基于name来删的,而不是index,所以用index来作为FlatList的Item的key时是会合世bug的。

_keyExtractor = (item,index)=> { 
 // 千万别用index,不然在删购物车数据时,如果从第一个item开始删会产生节点渲染错乱的bug 
 return item.name
}

依赖github项目地址: 
github.com/shooterRao/…

总结

如上所述是笔者给我们介绍的用React-Native+Mobx做叁个Mini水果市廛APP(附源码),希望对我们持有援救,假使我们有别的疑问请给本人留言,我会及时还原我们的。在此也极度多谢我们对台本之家网址的辅助!

你大概感兴趣的稿子:

  • React-Native使用Mobx落成购物车功效

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website