Coder Social home page Coder Social logo

mobx-vue's Introduction

mobx-vue

Build Status npm version coverage npm downloads

Vue2 bindings for MobX, inspired by mobx-react. Looking for Vue3 support? Please look here mobx-vue-lite

logo

Support Table

package mobx v6 mobx v2/v3/v4/v5 vue2 vue3
mobx-vue >= v2.1.0 * (exclude v2.1.0) * -
mobx-vue-lite * - - *

* means all and - means none

Installation

npm i mobx-vue -S

or

yarn add mobx-vue

Why mobx-vue

MobX is an unopinionated, scalable state management, which can make our programming more intuitive.

Unlike the other vue-rx-inspired libraries which based on the plugin mechanism of vue, mobx-vue will be the simplest you ever meet. What you all need is to bind your state in component definition and observe it just like mobx-react does, then your component will react to your state changes automatically which managed by mobx.

And, the most important is that you can build a view-library-free application, if you wanna migrate to another view library(React/Angular) someday, rewrite the template and switch to the relevant mobx bindings(mobx-react,mobx-angular,mobx-angularjs) is all you have to do.

Articles:

Usage

We highly recommend using the bindings with vue-class-component decorator, and define the Store/ViewModel independently.

import { action, computed, observable } from "mobx";
export default class ViewModel {
    @observable age = 10;
    @observable users = [];

    @computed get computedAge() {
        return this.age + 1;
    }

    @action.bound setAge() {
        this.age++;
    }
    
    @action.bound async fetchUsers() {
    	this.users = await http.get('/users')
    }
}
<template>
    <section>
        <p v-text="state.age"></p>
        <p v-text="state.computedAge"></p>
        <p v-for="user in state.users" :key="user.name">{{user.name}}</p>
        <button @click="state.setAge"></button>
    </section>
</template>

<script lang="ts">
    import Vue from "vue";
    import Component from "vue-class-component";
    import { Observer } from "mobx-vue";
    import ViewModel from "./ViewModel";

    @Observer
    @Component
    export default class App extends Vue {
        state = new ViewModel()
        mounted() { 
            this.state.fetchUsers();
        }
    }
</script>

Or used with the traditional way:

<script lang="ts">
    import { observer } from "mobx-vue";
    import ViewModel from "./ViewModel";

    export default observer({
        data() {
            return { state: new ViewModel() }
        },
        mounted() {
            this.state.fetchUsers() 
        }
    })
</script>

All you need is to bind your state to component and observe it. No more reactive data definitions in component.

Tips: If you're tired of instantiating instance manually every time, you might wanna try the mmlpx library which leveraged a dependency injection system.

API

  • observer((VueComponent | options): ExtendedVueComponent

mobx-vue's People

Contributors

azzra avatar dependabot[bot] avatar doxiaodong avatar ichenlei avatar kuitos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mobx-vue's Issues

[RFC] mobx-vue v3

Introduction

Since mobx v6 and vue v3 was released for a while, it's time to support mobx v6 and vue v3.

Current Feature Request

Vue 3 Compatibility #50
Module Browser Build #49
Can I use mobx-vue without decorated class properties? #47
Add Documentation re Optimizing Rendering #36
Nuxt support #26 #32

Deprecate vue-class-component

Thoughts about mobx-vue #3 (comment)

I'm kinda wondering what direction this project might take with a "new vue" on the horizon. Since vue seems to move away from classes and therefore decorators is there a happy path to using mobx with vue in 2020?

vue-class-component has their own problem
vuejs/vue-class-component#406

Naive proto design (only for vue3)

No.1 idea

export a vue plugin for vue3

import { createApp } from 'vue';
import MobxVuePlugin from 'mobx-vue';
import App from './App.vue';

createApp(App)
    .use(MobxVuePlugin, { name: 'MyObserver' /** optional Observer component name */ })
    .mount('#app');

Using Observer Component in your app

<template>
  <Observer>
    <div>name: {{data.name}}</div>
    <button @click="changeName">change</button>
  </Observer>
</template>

<script setup>
  import { runInAction, observable } from mobx;
  const data = observable({ name: "iChenLei" });
  const changeName = () => {
   runInAction(() => { data.name = "Kuitos" });
 };
</script>

No.2 idea

export a observer function to hack vue export default, no need to install as plugin, you can use it directly.

<template>
  <UserComponent />
  <ProfileComponent />
</template>

<script>
  import { observer } from 'mobx-vue';
  export default observer({
      setup() {
        // balabala
      }
  });
</script>

<!-- use @ decorator -->
<script>
  import { observer } from 'mobx-vue';
  @observer
  export default {
      setup() {
        // balabala
      }
  };
</script>

But it not easy to do this , we need hack the vue @internel (e.g. $, $options, $forceUpdate)

// vue 3 internal component properties map
const publicPropertiesMap: PublicPropertiesMap = extend(Object.create(null), {
  $: i => i,
  $el: i => i.vnode.el,
  $data: i => i.data,
  $props: i => (__DEV__ ? shallowReadonly(i.props) : i.props),
  $attrs: i => (__DEV__ ? shallowReadonly(i.attrs) : i.attrs),
  $slots: i => (__DEV__ ? shallowReadonly(i.slots) : i.slots),
  $refs: i => (__DEV__ ? shallowReadonly(i.refs) : i.refs),
  $parent: i => getPublicInstance(i.parent),
  $root: i => getPublicInstance(i.root),
  $emit: i => i.emit,
  $options: i => (__FEATURE_OPTIONS_API__ ? resolveMergedOptions(i) : i.type),
  $forceUpdate: i => () => queueJob(i.update),
  $nextTick: i => nextTick.bind(i.proxy!),
  $watch: i => (__FEATURE_OPTIONS_API__ ? instanceWatch.bind(i) : NOOP)
} as PublicPropertiesMap)

And when you use <script setup>, it's also diffcult to implement a useful observer function.(any suggestion for this welcome)

No.3 idea

add the template sugar like <template pug>, <template functional>.

<tempalte observer>
  <UserProfile />
<template>

It looks like best solution for mobx-vue and vue sfc? But it need mobx-vue maintainer to maintian different tools to support this feature. (e.g. mobx-vue-loader for webpack, rollup-plugin-mobx-vue for vite and rollup, ...etc). Not a reliable choice i think. I didn't see any api allow us to change the <template> behavior. It's parsed by @vue/compiler-sfc and no public api.

My opinion

Deprecate vue-class-component and make vue sfc as first class support.

Summary

Any idea and disscussion welcome, maybe you can design the final mobx-vue v3 api.

Issue with NuxtJS Pages

When creating a page (i.e. a component under the pages directory) in NuxtJS, this is undefined in data() when the component is an observer.

如果有多个Store,该如何使用呢?

<template>
    <section>
        <p v-text="age"></p>
        <p v-text="computedAge"></p>
        <p v-for="user in users" :key="user.name">{{user.name}}</p>
        <button @click="setAge"></button>
    </section>
</template>

<script lang="ts">
    import { Connect } from "mobx-vue";
    import Vue from "vue";
    import Component from "vue-class-component";
    import ViewModel from './ViewModel';

    @Connect(new ViewModel())
    @Component()
    export default class App extends Vue {
        mounted() { 
            this.fetchUsers();
        }
    }
</script>

这是目前官方给的例子,想问下如果有多个Mobx 的store,该如何使用呢?我这样的使用可以被支持吗?

<template>
    <section>
        <p v-text="age"></p>
        <p v-text="computedAge"></p>
        <p v-for="user in users" :key="user.name">{{user.name}}</p>
        <button @click="setAge"></button>
       <p>{{ViewModel.user}}</p>
    </section>
</template>

<script lang="ts">
    import { Connect } from "mobx-vue";
    import Vue from "vue";
    import Component from "vue-class-component";
    import ViewModel from './ViewModel';
    import ViewModel2 from './ViewModel2';

    @Connect()
    @Component()
    export default class App extends Vue {
        mounted() { 
            this.fetchUsers();
        }
       computed:{
            user(){
               return ViewModel.user
           },
           user2(){
                return ViewModel2.user
          }
      }
    }
</script>

I got error when I use @Observer before @Component with Nuxtjs

I use Nuxt + typescript and run with 'universal' mode.

I got error when I refresh browser with this code

@Observer
@Component()
export default class Home extends Vue {
  mounted () {
  }
}

Is don't have any error when I try with

export default observer({
  mounted () {
  }
});

error TypeError: Cannot convert undefined or null to object
at Function.getOwnPropertyNames ()
at Home.Component._init (vendors.app.js:2672)
at Home.Vue (commons.app.js:16802)
at new Home (inspire.js:64)
at collectDataFromConstructor (vendors.app.js:2692)
at data (vendors.app.js:2767)
at collectData (inspire.js:28)
at VueComponent.data (inspire.js:111)
at VueComponent.Component.options.data (app.js:3171)
at getData (commons.app.js:16467)

[Vue warn]: The "data" option should be a function

This is my code

<template>
    <div class="test">
        <p>{{testMobx.data}}</p>
    </div>
</template>

<script lang="ts">
import Vue from 'vue'
import Component from 'vue-class-component'
import { Observer } from 'mobx-vue'
import { observable } from 'mobx'

class TestMobx {
    @observable data = 123
}

let TestClass = new TestMobx()

@Observer
@Component
export default class App extends Vue {
    testMobx = TestClass
}
</script>

but has this warning:

[Vue warn]: The "data" option should be a function that returns a per-instance value in component definitions.

why??

Doesn't appear to work with mobx-state-tree. Should it?

I create two stores, one a mobx store and one a mobx-state-tree store. I then hooked them both up to a vue view, per the readme. The mobx store works (i.e component updates when the store changes), but the view does not update on changes in the mobx-state-tree store.

No re-renders when observed state is used in some parts of the template

Hello,

First of all, I am quite new to Vue, so I might have missed some documentation. I have used Mobx in combination with React, and haven't had any issues there regarding views not being updated.

Secondly, I have a quite complex construction, where I inject my stores via Inversify in order to have some sort of IoC-layer between my components and stores.
This works, however, correctly as far as I can see.

But, it works as long as I use the observable properties from my stores directly in my template. Using the @Observer-decorator, my views update and everything is fine.

However, I am using vee-validate for validating my forms. I have Vee-validate's observer around my form, in which a button exists. This button is listening to a loading state in my store, and should be updated whenever the loading state changes.

For example:
There is a login form, when clicking the button Vee-validate's observer checks whether all required inputs are filled.
When that is the case, the login method is called. This login method triggers a method in the store. At that point, the loading state is set to loading, which should update the button in the template.
The store method does some things with the API to check whether the user can be logged in, whereafter the loading state is set to "loaded".

And there it goes wrong. I can see in my template that the state goes back to loading. But it doesn't switch to "loading" in Vee-validate's observer. I have to change an input in order to trigger a re-render, before the button is switched back to the already switched store loading state.
This can be seen in this video: https://imgur.com/a/1NsNevz
The true/false is the same loading state indicator as is used in the button. As can be seen, the true/false at the bottom is updated when the action is done loading. The button however doesn't update until I type.
You can also see some error messages appearing at the top. These are also handles with stores and injected in components.

The code for the Vue component I am using:
https://gist.github.com/emachiels/81fd1b0e552bb536d499db75b3dd153f

And the code for the cocStore:
https://gist.github.com/emachiels/ae7ca3d6fcbea6e3c06644cef0368df6

Does someone know what the problem is?
Thanks in advance!

-- EDIT --
I am using the following versions:
Mobx: 5.15.4
Mobx-Vue: 2.0.10
Vue: 2.6.11
Vee-validate: 3.3.3

Most of the tests are failing

I was gearing up to start a PR but noticed the bulk of the tests are failing? Is there something wrong with the lib, or are the tests outdated?

Exception when using with mobx-state-tree

Hello,
I'm facing a strange exception, and I was able to reproduce it in the tests below. I'm writing here as the test made without mobx-vue doesn't fail, so I presume the problem starts from there.

The environment is:
mobx: 5.15.14
mobx-state-tree: 3.17.2
mobx-vue: 2.0.10
vue: 2.6.11

import { mount } from "@vue/test-utils";
import { observer } from "mobx-vue";
import { types, applySnapshot } from "mobx-state-tree";
import { Component } from "vue-property-decorator";
import Vue from 'vue'
const baseType = types.model({
    addresses: types.array(
      types.model({
        city: types.maybeNull(types.string)
      })
    )
  });

@Component({
  name: "sc",
  template: `<div>
  <span v-for="(a, idx) in context.addresses" :key="idx">{{a.city}}</span>
</div>`
})
class Sc extends Vue{
   context = baseType.create()
  };
describe("Vue skel store", () => {
  it("works alone", () => {
    let ctx = baseType.create();
    expect(ctx).toBeDefined();
    expect(ctx.addresses.length).toBe(0);
    applySnapshot(ctx, { addresses: [{ city: "Rome" }] });
    expect(ctx.addresses.length).toBe(1);
    expect(ctx.addresses[0].city).toBe("Rome");
  });
  it("doesn't work when used inside Vue", () => {
    const wrapper = mount(observer(Sc));
    expect(wrapper.text()).toBe("");
    expect(wrapper.vm.context).toBeDefined();
    expect(wrapper.vm.context.addresses.length).toBe(0);
    applySnapshot(wrapper.vm.context, { addresses: [{ city: "Rome" }] });
    expect(wrapper.text()).toBe("Rome");
    expect(wrapper.vm.context.addresses.length).toBe(1);
    wrapper.destroy();
  });
});

the exception raised is:

    TypeError: child.finalizeCreation is not a function
        at /home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1601:27
        at ObjectNode.Object.<anonymous>.BaseNode.baseFinalizeCreation (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1141:17)
        at ObjectNode.Object.<anonymous>.ObjectNode.finalizeCreation (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1596:14)
        at ObjectNode.Object.<anonymous>.ObjectNode.createObservableInstance (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1383:14)
        at executeAction (/home/dev/project/node_modules/mobx/lib/mobx.js:908:19)
        at ObjectNode.createObservableInstance (/home/dev/project/node_modules/mobx/lib/mobx.js:895:16)
        at ObjectNode.Object.<anonymous>.ObjectNode.createObservableInstanceIfNeeded (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1320:18)
        at ModelType.Object.<anonymous>.ComplexType.getValue (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1916:14)
        at ObjectNode.get (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1020:30)
        at ObjectNode.Object.<anonymous>.ObjectNode.unbox (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1587:44)
        at ObservableArrayAdministration.Object.<anonymous>.ObservableArrayAdministration.dehanceValue (/home/dev/project/node_modules/mobx/lib/mobx.js:3057:25)
        at Array.get (/home/dev/project/node_modules/mobx/lib/mobx.js:3318:28)
        at Object.get (/home/dev/project/node_modules/mobx/lib/mobx.js:3004:40)
        at dependArray (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:1134:14)
        at ObjectNode.reactiveGetter [as storedValue] (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:1033:13)
        at ArrayType.Object.<anonymous>.ComplexType.getValue (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1917:21)
        at ObjectNode.get (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1020:30)
        at ObjectNode.Object.<anonymous>.ObjectNode.unbox (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1587:44)
        at ObservableValue.Object.<anonymous>.ObservableValue.dehanceValue (/home/dev/project/node_modules/mobx/lib/mobx.js:1020:25)
        at ObservableValue.Object.<anonymous>.ObservableValue.get (/home/dev/project/node_modules/mobx/lib/mobx.js:1072:21)
        at ObservableObjectAdministration.Object.<anonymous>.ObservableObjectAdministration.read (/home/dev/project/node_modules/mobx/lib/mobx.js:3958:37)
        at Object.get (/home/dev/project/node_modules/mobx/lib/mobx.js:4197:36)
        at Object.reactiveGetter [as addresses] (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:1027:35)
        at Proxy.eval (eval at createFunction (/home/dev/project/node_modules/vue-template-compiler/build.js:4638:12), <anonymous>:1:40)
        at VueComponent.Vue._render (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:3538:22)
        at VueComponent.updateComponent (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:4054:21)
        at Watcher.get (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:4465:25)
        at Watcher.run (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:4540:22)
        at flushSchedulerQueue (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:4298:13)
        at queueWatcher (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:4387:9)
        at Watcher.update (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:4530:5)
        at Dep.notify (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:732:13)
        at Array.mutator (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:884:12)
        at ObservableArrayAdministration.Object.<anonymous>.ObservableArrayAdministration.spliceItemsIntoValues (/home/dev/project/node_modules/mobx/lib/mobx.js:3151:46)
        at ObservableArrayAdministration.Object.<anonymous>.ObservableArrayAdministration.spliceWithArray (/home/dev/project/node_modules/mobx/lib/mobx.js:3143:24)
        at Proxy.replace (/home/dev/project/node_modules/mobx/lib/mobx.js:3222:20)
        at ArrayType.Object.<anonymous>.ArrayType.applySnapshot (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:4453:16)
        at executeAction (/home/dev/project/node_modules/mobx/lib/mobx.js:908:19)
        at ArrayType.applySnapshot (/home/dev/project/node_modules/mobx/lib/mobx.js:895:16)
        at /home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1633:30
        at executeAction (/home/dev/project/node_modules/mobx/lib/mobx.js:908:19)
        at <unnamed action> (/home/dev/project/node_modules/mobx/lib/mobx.js:895:16)
        at runMiddleWares (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:2597:40)
        at runWithActionContext (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:2460:16)
        at ObjectNode.res (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:2487:16)
        at ObjectNode.Object.<anonymous>.ObjectNode.applySnapshot (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1316:14)
        at ArrayType.Object.<anonymous>.ComplexType.tryToReconcileNode (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1938:21)
        at ArrayType.Object.<anonymous>.ComplexType.reconcile (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1944:35)
        at OptionalValue.Object.<anonymous>.OptionalValue.reconcile (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:5562:30)
        at Array.Object.<anonymous>.ModelType.willChange (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:4905:41)
        at interceptChange (/home/dev/project/node_modules/mobx/lib/mobx.js:2957:37)
        at ObservableObjectAdministration.Object.<anonymous>.ObservableObjectAdministration.write (/home/dev/project/node_modules/mobx/lib/mobx.js:3969:26)
        at Object.set (/home/dev/project/node_modules/mobx/lib/mobx.js:4200:29)
        at Object.reactiveSetter [as addresses] (/home/dev/project/node_modules/vue/dist/vue.runtime.common.dev.js:1052:16)
        at /home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:4972:36
        at /home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:5001:60
        at Array.forEach (<anonymous>)
        at ModelType.Object.<anonymous>.ModelType.forAllProps (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:5001:28)
        at ModelType.Object.<anonymous>.ModelType.applySnapshot (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:4971:14)
        at executeAction (/home/dev/project/node_modules/mobx/lib/mobx.js:908:19)
        at ModelType.applySnapshot (/home/dev/project/node_modules/mobx/lib/mobx.js:895:16)
        at /home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1633:30
        at executeAction (/home/dev/project/node_modules/mobx/lib/mobx.js:908:19)
        at <unnamed action> (/home/dev/project/node_modules/mobx/lib/mobx.js:895:16)
        at runMiddleWares (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:2597:40)
        at runWithActionContext (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:2460:16)
        at ObjectNode.res (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:2487:16)
        at ObjectNode.Object.<anonymous>.ObjectNode.applySnapshot (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:1316:14)
        at applySnapshot (/home/dev/project/node_modules/mobx-state-tree/dist/mobx-state-tree.js:406:37)
        at Object.<anonymous> (/home/dev/project/src/datastore/__tests__/vue-store-skel.spec.js:41:5)
        at Object.asyncJestTest (/home/dev/project/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
        at /home/dev/project/node_modules/jest-jasmine2/build/queueRunner.js:43:12
        at new Promise (<anonymous>)
        at mapper (/home/dev/project/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
        at /home/dev/project/node_modules/jest-jasmine2/build/queueRunner.js:73:41
        at processTicksAndRejections (internal/process/task_queues.js:97:5)

but before this, another exception is catched

    TypeError: mobx.getAtom(...).reportObserved is not a function

      39 |     expect(wrapper.vm.context).toBeDefined();
      40 |     expect(wrapper.vm.context.addresses.length).toBe(0);
    > 41 |     applySnapshot(wrapper.vm.context, { addresses: [{ city: "Rome" }] });
         |     ^
      42 |     expect(wrapper.text()).toBe("Rome");
      43 |     expect(wrapper.vm.context.addresses.length).toBe(1);
      44 |     wrapper.destroy();

      at node_modules/mobx-state-tree/dist/mobx-state-tree.js:4947:50
      at node_modules/mobx-state-tree/dist/mobx-state-tree.js:5001:60
          at Array.forEach (<anonymous>)
      at ModelType.Object.<anonymous>.ModelType.forAllProps (node_modules/mobx-state-tree/dist/mobx-state-tree.js:5001:28)
      at ModelType.Object.<anonymous>.ModelType.getSnapshot (node_modules/mobx-state-tree/dist/mobx-state-tree.js:4946:14)
      at ObjectNode.Object.<anonymous>.ObjectNode._getActualSnapshot (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1487:26)
      at ObjectNode.Object.<anonymous>.ObjectNode.getSnapshot (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1483:20)
      at ObjectNode.get (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1473:32)
      at trackDerivedFunction (node_modules/mobx/lib/mobx.js:757:24)
      at ComputedValue.Object.<anonymous>.ComputedValue.computeValue (node_modules/mobx/lib/mobx.js:1251:19)
      at ComputedValue.Object.<anonymous>.ComputedValue.trackAndCompute (node_modules/mobx/lib/mobx.js:1236:29)
      at invalidateComputed (node_modules/mobx-state-tree/dist/mobx-state-tree.js:3400:10)
      at ObjectNode.Object.<anonymous>.ObjectNode.createObservableInstance (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1377:9)
      at executeAction (node_modules/mobx/lib/mobx.js:908:19)
      at ObjectNode.createObservableInstance (node_modules/mobx/lib/mobx.js:895:16)
      at ObjectNode.Object.<anonymous>.ObjectNode.createObservableInstanceIfNeeded (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1320:18)
      at ModelType.Object.<anonymous>.ComplexType.getValue (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1916:14)
      at ObjectNode.get (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1020:30)
      at ObjectNode.Object.<anonymous>.ObjectNode.unbox (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1587:44)
      at ObservableArrayAdministration.Object.<anonymous>.ObservableArrayAdministration.dehanceValue (node_modules/mobx/lib/mobx.js:3057:25)
      at Array.get (node_modules/mobx/lib/mobx.js:3318:28)
      at Object.get (node_modules/mobx/lib/mobx.js:3004:40)
      at dependArray (node_modules/vue/dist/vue.runtime.common.dev.js:1134:14)
      at ObjectNode.reactiveGetter [as storedValue] (node_modules/vue/dist/vue.runtime.common.dev.js:1033:13)
      at ArrayType.Object.<anonymous>.ComplexType.getValue (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1917:21)
      at ObjectNode.get (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1020:30)
      at ObjectNode.Object.<anonymous>.ObjectNode.unbox (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1587:44)
      at ObservableValue.Object.<anonymous>.ObservableValue.dehanceValue (node_modules/mobx/lib/mobx.js:1020:25)
      at ObservableValue.Object.<anonymous>.ObservableValue.get (node_modules/mobx/lib/mobx.js:1072:21)
      at ObservableObjectAdministration.Object.<anonymous>.ObservableObjectAdministration.read (node_modules/mobx/lib/mobx.js:3958:37)
      at Object.get (node_modules/mobx/lib/mobx.js:4197:36)
      at Object.reactiveGetter [as addresses] (node_modules/vue/dist/vue.runtime.common.dev.js:1027:35)
      at Proxy.eval (eval at createFunction (node_modules/vue-template-compiler/build.js:4638:12), <anonymous>:1:40)
      at VueComponent.Vue._render (node_modules/vue/dist/vue.runtime.common.dev.js:3538:22)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4054:21)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4465:25)
      at Watcher.run (node_modules/vue/dist/vue.runtime.common.dev.js:4540:22)
      at flushSchedulerQueue (node_modules/vue/dist/vue.runtime.common.dev.js:4298:13)
      at queueWatcher (node_modules/vue/dist/vue.runtime.common.dev.js:4387:9)
      at Watcher.update (node_modules/vue/dist/vue.runtime.common.dev.js:4530:5)
      at Dep.notify (node_modules/vue/dist/vue.runtime.common.dev.js:732:13)
      at Array.mutator (node_modules/vue/dist/vue.runtime.common.dev.js:884:12)
      at ObservableArrayAdministration.Object.<anonymous>.ObservableArrayAdministration.spliceItemsIntoValues (node_modules/mobx/lib/mobx.js:3151:46)
      at ObservableArrayAdministration.Object.<anonymous>.ObservableArrayAdministration.spliceWithArray (node_modules/mobx/lib/mobx.js:3143:24)
      at Proxy.replace (node_modules/mobx/lib/mobx.js:3222:20)
      at ArrayType.Object.<anonymous>.ArrayType.applySnapshot (node_modules/mobx-state-tree/dist/mobx-state-tree.js:4453:16)
      at executeAction (node_modules/mobx/lib/mobx.js:908:19)
      at ArrayType.applySnapshot (node_modules/mobx/lib/mobx.js:895:16)
      at node_modules/mobx-state-tree/dist/mobx-state-tree.js:1633:30
      at executeAction (node_modules/mobx/lib/mobx.js:908:19)
      at <unnamed action> (node_modules/mobx/lib/mobx.js:895:16)
      at runMiddleWares (node_modules/mobx-state-tree/dist/mobx-state-tree.js:2597:40)
      at runWithActionContext (node_modules/mobx-state-tree/dist/mobx-state-tree.js:2460:16)
      at ObjectNode.res (node_modules/mobx-state-tree/dist/mobx-state-tree.js:2487:16)
      at ObjectNode.Object.<anonymous>.ObjectNode.applySnapshot (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1316:14)
      at ArrayType.Object.<anonymous>.ComplexType.tryToReconcileNode (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1938:21)
      at ArrayType.Object.<anonymous>.ComplexType.reconcile (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1944:35)
      at OptionalValue.Object.<anonymous>.OptionalValue.reconcile (node_modules/mobx-state-tree/dist/mobx-state-tree.js:5562:30)
      at Array.Object.<anonymous>.ModelType.willChange (node_modules/mobx-state-tree/dist/mobx-state-tree.js:4905:41)
      at interceptChange (node_modules/mobx/lib/mobx.js:2957:37)
      at ObservableObjectAdministration.Object.<anonymous>.ObservableObjectAdministration.write (node_modules/mobx/lib/mobx.js:3969:26)
      at Object.set (node_modules/mobx/lib/mobx.js:4200:29)
      at Object.reactiveSetter [as addresses] (node_modules/vue/dist/vue.runtime.common.dev.js:1052:16)
      at node_modules/mobx-state-tree/dist/mobx-state-tree.js:4972:36
      at node_modules/mobx-state-tree/dist/mobx-state-tree.js:5001:60
          at Array.forEach (<anonymous>)
      at ModelType.Object.<anonymous>.ModelType.forAllProps (node_modules/mobx-state-tree/dist/mobx-state-tree.js:5001:28)
      at ModelType.Object.<anonymous>.ModelType.applySnapshot (node_modules/mobx-state-tree/dist/mobx-state-tree.js:4971:14)
      at executeAction (node_modules/mobx/lib/mobx.js:908:19)
      at ModelType.applySnapshot (node_modules/mobx/lib/mobx.js:895:16)
      at node_modules/mobx-state-tree/dist/mobx-state-tree.js:1633:30
      at executeAction (node_modules/mobx/lib/mobx.js:908:19)
      at <unnamed action> (node_modules/mobx/lib/mobx.js:895:16)
      at runMiddleWares (node_modules/mobx-state-tree/dist/mobx-state-tree.js:2597:40)
      at runWithActionContext (node_modules/mobx-state-tree/dist/mobx-state-tree.js:2460:16)
      at ObjectNode.res (node_modules/mobx-state-tree/dist/mobx-state-tree.js:2487:16)
      at ObjectNode.Object.<anonymous>.ObjectNode.applySnapshot (node_modules/mobx-state-tree/dist/mobx-state-tree.js:1316:14)
      at applySnapshot (node_modules/mobx-state-tree/dist/mobx-state-tree.js:406:37)
      at Object.<anonymous> (src/datastore/__tests__/vue-store-skel.spec.js:41:5)

If necessary, I can provide a more detailed stack-trace

Thoughts about mobx-vue

Hi there! I've read the docs and came with some ideas:

  1. Mostly what we want is to connect Store instance with Component instance/element, instead of to connect Store instance with Component class/definition. The later may introduce issues when the same Component class/definition used (instantiated) multiple times - different Component instance/element will share the same state. Such behavior suits global-state-situation, while not local-viewModal-state-situation.

  2. Maybe connect is not a good idea. connect defines extra properties on Vue vm in an implicit way. IMO, it will be better not to provide the ability of connect. Offering ability of observer allows developer to organize their state in more explicit way, maybe something like:

    @observer // assuming that we provide `observer` like mobx-react
    @Component()
    export default class App extends Vue {
      viewModel = new ViewModel() // which means `viewModel.age` in template
    }

What do you think?

如何在全局创建一个store?

如何在全局创建一个store?如何像vuex一样创建一个全局的store,例如:

import store from './app/vuex/store.js';//引用store
import router from './app/router/router.js';//引用router
//创建vue实例
window.myvue = new Vue({
    el: '#app'
    ,store:store//把store给root vue
    ,router:router //通过vue配置中的router挂载router实例
    ,render: h => h(App)
});

创建一个全局组件后,在每一个组件子组件中都可以通过this.$store来获取这个store。

目前mobx-vue似乎只能针对一个vue创建一个store,子组件不知道用什么方法获取这个呢?

Some doubt

How can i detect mobx variable changing in vue ? Thx

Proposal for Official Support

Hi @mweststrate
Recently I built this library to combine mobx with vue through a simple api what like mobx-react does, now it released v1.0.0 and reached 100% code coverage already, could you please donate some time to make a review and check is it good enough to move to mobxjs organization?

Need boilerplate projects

Need some start boilerplates vue with mobx-vue. I tried to put to work vue-cli 3.0.4 (vue 2.5.17) with mobx-vue 2.0.7 and mobx 5.5.0 with babel or ts and had unsolved problems with decorators:

vue.runtime.esm.js?2b0e:587 [Vue warn]: Error in data(): "Error: Decorating class property failed. Please ensure that proposal-class-properties is enabled and set to use loose mode. To use proposal-class-properties in spec mode with decorators, wait for the next major version of decorators in stage 2."

found in

---> <Object> at src/App.vue
       <Root>
warn @ vue.runtime.esm.js?2b0e:587
vue.runtime.esm.js?2b0e:1737 Error: Decorating class property failed. Please ensure that proposal-class-properties is enabled and set to use loose mode. To use proposal-class-properties in spec mode with decorators, wait for the next major version of decorators in stage 2.
    at _initializerWarningHelper (initializerWarningHelper.js?ec94:2)
    at new ViewModel (ViewModel.js?b643:2)
    at VueComponent.data (App.vue?234e:7)
    at collectData (collectData.js?2679:8)
    at VueComponent.data (observer.js?3172:17)
    at getData (vue.runtime.esm.js?2b0e:3413)
    at initData (vue.runtime.esm.js?2b0e:3370)
    at initState (vue.runtime.esm.js?2b0e:3307)
    at VueComponent.Vue._init (vue.runtime.esm.js?2b0e:4624)
    at new Object (vue.runtime.esm.js?2b0e:4794)
logError @ vue.runtime.esm.js?2b0e:1737

store as vue instance prototype

I want to ask if this will work if i added a new instance of my store as a vue instance prototype object
Vue.prototype.$todoStore=new TodoViewModel()
And use in all my components as this.$todoStore as opposed to creating a new instance of the store in each component.

`observer.js` missing from current dist/package

Hello,

I've installed the package from npm and i was getting this error:

Module not found: Error: Can't resolve './observer' in '/Users/........./node_modules/mobx-vue/esm'

The file seems to be missing indeed from the package after checking node_modules.
Captură de ecran din 2020-08-05 la 09 24 15

This is just a heads-up! I had to (re)build the project and move the files manually to get it working. Other than that, nice job!

meta component

I have a case where I pass component definition directly to vue meta component.

<component :is="{..component definition}" v-bind="props"/>

wrapping it with observer breaks a whole thing.
Do you have any idea what to do here?

Dev tooling?

Thank you for all your work with Mobx-vue and helping make Mobx work with Vue! I've just started working with MobX + Vue and liking it a lot so far.

One question I had was about any dev tooling available that is stack agnostic or Vue specific, as it looks like the Mobx chrome dev tools and Delorean for time travel debugging is Mobx + React only? If there isn't any at the moment, are there plans for this in the future?

What's the counterpart of `Vue.nextTick` for mobx-vue?

When unit testing Vue components, it is often necessary to call await vm.$nextTick() to ensure the rerender is completed before accessing the component's DOM for assertion.

As far as I understand, mobx-vue triggers the rerender directly, and has nothing to do with Vue's Async Update Queue. Therefore vm.$nextTick would not work for mobx-vue.

So the question is how would one do the same thing as nextTick with mobx-vue?

Ideas regarding slot related problems

We run into a common problem using this library and vuetifyjs, and I think this library could be extended to offer a few basic components/functions to solve those issues with $scopedSlots. Bear with me for more detail.

When using a component such as v-data-table, we have a mandatory scoped slot. The problem with scope slots is that they're not part of the render of the current component, by current I mean the one we see in our SFC. Anything within a scoped slot is rendered as part of the render of the child component. So outside of the @Observer 'guard'.

My thought process was, maybe we could add a function to this library, that given a component wraps all $scopedSlots with a observer annotation? So that way, instead of using the v-data-table component from vuetify, I would use my data-table component which would be something akin to:

import { observerWithScopedSlots } from 'mobx-vue';
import VDataTable from 'vuetify/lib/components/VDataTable/VDataTable';

export default DataTable = observerWithScopedSlots(VDataTable);

Thoughts?

How to enable mobx strict mode?

here is the code, models

import { action, observable, configure } from "mobx";

configure({enforceActions: true}) // strict mode

export default class ViewModel {
  @observable data = [];
  @action.bound load() {
    setTimeout(() => {
      this.data = [{ age: 18, name: 'aaa' }, { age: 19, name: 'bbb' }]
    }, 1500);
  }
}

vue component:

<template>
	<div class="hello">
		<pre>
      {{state.data}}
    </pre>
	</div>
</template>

<script>
import { observer } from "mobx-vue";
import User from '../models/test.js'
export default observer({
  name: 'HelloWorld',
  mounted() {
    this.state.load()
  },
  data() {
    return {
      state: new User()
    }
  }
})
</script>

Uncaught Error: [mobx] Since strict-mode is enabled, chang...
The mobx strict mode is useful to prevent some unnecessary problem. I tried add action in vue compoent, it doesn't work, has any way to enable strict model ?

mobx cause [Vue warn]: Unknown custom element: <router-link>

It was working fine before installing and add mobx to vuejs
main.js

import Vue from 'vue/dist/vue.js'
import VueRouter from 'vue-router'

import App from './components/app/App'
import router from './routers'

Vue.use(VueRouter)
Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

App Template:

<div id="app">
  <router-link to="/home">Home</router-link> 
  <router-view></router-view>
</div>

App Script:


import { observer } from 'mobx-vue';
import Store from '../../stores/store';

export default observer({
  name: 'app',
  components: {},
  data() {
    return { state: new Store() };
  }
});

I've got this error:

[Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.

vue.runtime.esm.js?2b0e:619 [Vue warn]: Unknown custom element: - did you register the component correctly? For recursive components, make sure to provide the "name" option.

any guide about isomorphic mobx with vue???

Hi guys, I'd like to use mobx and vue isomorphic, which is the best way to achieve this??? would be great if mubx would be incorporated to nuxt as alternative to redux...any plan for this???

thank you guys for the great job...

Mobx6 not working

When I download mobx-vue, it comes with version 2.0.11 and doesn't work with mobx6, it gives a webpack object error in the console.
Does this plugin work for vue.js with mobx 6 without using @pattern?

Thanks.

What about Provider decorator?

Hey,
Is any common pattern for mobx-vue which is similar to using Provider decorator from a mobx-react?

Also I have a question about using it with routing. Can I inject specified substore/ substores to exact route?

加入v-if后Dom响应失效

<template>
	<div class = "overlook-setting">
		<div v-if = "isShow">
			{{testMobx.data}}
		</div>
	
	</div>
</template>
<script>
  // import { Component, Vue } from 'vue-property-decorator'
  import { observable, action } from 'mobx'
  import { observer } from 'mobx-vue'

  class TestMobx {
    constructor() {
    }

    @observable data = {
      x: 0,
      y: 0,
      z: 0,
    }

    @action.bound setData() {
      this.data.x++
    }
  }

  let TestClass = new TestMobx()
  setTimeout(() => {
    TestClass.setData()

  }, 3000)

  export default observer({
    data() {
      return {
        isShow: false,
        testMobx: TestClass
      }
    },
    mounted() {
      this.isShow = true
    }
  })
</script>
<style lang = "less" scoped>
	.overlook-setting {
	}
</style>

当模板渲染加入v-if后,Dom响应会失效。

页面理论应该在2000后显示 {x:1} ,可是一直会停在{x:0}

视图层不响应,我这样有问题吗?

<template>
	<div class = "overlook-setting">
		{{testMobx.data}}
	</div>
</template>
<script>
  import { Component, Vue } from 'vue-property-decorator'
  import { observable, action } from 'mobx'

  class TestMobx {
    constructor() {
    }

    @observable data = 9

    @action.bound setData() {
      this.data++
    }
  }

  let TestClass = new TestMobx()

  @Component
  export default class OverlookSetting extends Vue {
    testMobx = TestClass

    mounted() {
      this.testMobx.setData()
      this.testMobx.data++
      console.log(this.testMobx.data)

    }

    beforeDestroy() {
    }

  }
</script>
<style lang = "less" scoped>
	.overlook-setting {
	}
</style>


当我运行上面这段代码的时候打印出来的数据是10 ,但Dom还是9

Vue版本2.5.16 ,Mobx版本4.3.1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.