Search code examples
mobx-state-tree

How To Split Mobx State Tree Models Across Multiple Files?


I have a Mobx State Tree model that has grown too long and I would like to split it across multiple javascript files.

Here is a demo of some of the code:

///file1.js
 import { types } from "mobx-state-tree";

export const ExampleModel = types
.model("Example", {
    id: types.identifier,
    name: types.optional(types.string, ""),
    anotherName: types.optional(types.string, ""),

})
.views(self => ({
    get test() {
        return "test"
    }
}))
.views(self => ({
    get anotherTest() {
        return "anotherTest"
    }
}))
.actions(self => ({
    setName(name) {
        self.name = name
    }
}))
.actions(self => ({
    setAnotherName(name) {
        self.anotherName = name
    }
}))

What I want is to split this between two files, like:

///file1.js
import { types } from "mobx-state-tree";

export const ExampleModel = types
.model("Example", {
    id: types.identifier,
    name: types.optional(types.string, ""),
    anotherName: types.optional(types.string, ""),

})
.views(self => ({
    get test() {
        return "test"
    }
})) 
.actions(self => ({
    setName(name) {
        self.name = name
    }
}))


///file2.js
import { ExampleModel } from "./file1.js";
ExampleModel.views(self => ({
    get anotherTest() {
        return "anotherTest"
    }
})).actions(self => ({
    setAnotherName(name) {
        self.anotherName = name
    }
}))

You can see here that I am attempting to move a view and and action to a separate javascript file. I expect I need to do some kind of import and export between these two files, but I can't figure out how to do it.

I know that Mobx State Tree has compose functionality, as shown here: https://nathanbirrell.me/notes/composition-mobx-state-tree/

But I am afer something more simple than this... I don't want to set up multiple models, I just need the ability to spread a model across multiple javascript files.


Solution

  • We do that all the time.

    Just export your actions and views separately:

    // file1.js
    import { types } from "mobx-state-tree"
    
    export const props = {
        id: types.identifier,
        name: types.optional(types.string, ""),
        anotherName: types.optional(types.string, ""),
    
    }
    export const views = self => ({
        get test() {
            return "test"
        }
    })
    export const actions = self => ({
        setName(name) {
            self.name = name
        }
    })
    

    Then, create the final store from them:

    // store.js
    import { types } from "mobx-state-tree"
    import * as file1 from "./file1"
    import * as file2 from "./file2"
    
    const Store = types
      .model('Store')
      .props(file1.props)
      .views(file1.views)
      .actions(file1.actions)
      .props(file2.props)
      .views(file2.views)
      .actions(file2.actions)
    
    export default Store
    

    You can also create your own stores for testing, only from one file:

    // __tests__/file1.js
    import { types } from "mobx-state-tree"
    import { actions, views, props } from "./file1"
    
    const Store = types
      .model('Store')
      .props(props)
      .views(views)
      .actions(actions)
    const store = Store.create(myTestSnapshot)
    
    test('setName should set the name prop', () => {
      store.setName('john')
      expect(store.name).toBe('john')
    })