Mobx-state-tree: [Feature Request] Add ability to add static methods on a Model

Created on 6 Jan 2018  Â·  14Comments  Â·  Source: mobxjs/mobx-state-tree

Something like:

const Page = types.model("Page", {
  id: types.identifier(),
  lines: types.array(types.string),
}).static({
  async fetchSnapshot(api: apiClient, pageId: string) {
    return api.getPage(pageId);
  }
});

const Book = types.model("Book", {
  id: types.identifier(),
  name: types.string,
  pages: types.array(Page)
}).static({
  async fetchSnapshot(api: apiClient, bookId: string) {
    const {pagesIds, book} = await api.getBook(bookId);

    return {
      ...book,
      pages: await Promise.all(pagesIds.map(pageId => Book.fetchSnapshot(api, pageId)))
    }
  }
});

My immediate usecase is for data fetching and getting the initial snapshot of a model.

brainstorminwild idea

Most helpful comment

I want to point out that Backbone.extend has ability to add statics or "class properties "
http://backbonejs.org/#Model-extend

And my implementation for this feature as an external function:

export function MSTAddStatics<T extends IModelType<any, any>, TStatics extends object>(m: T, statics: TStatics) {
  Object.keys(statics).forEach(k => ((m as any)[k] = (statics as any)[k]));
  return m as T & TStatics;
}

All 14 comments

Would Book.fetchSnapshot = work as well?

Op za 6 jan. 2018 om 06:39 schreef Bnaya Peretz notifications@github.com:

Something like:

const Page = types.model("Page", {
id: types.identifier(),
lines: types.array(types.string),
}).static({
async fetchSnapshot(api: apiClient, pageId: string) {
return api.getPage(pageId);
}
});
const Book = types.model("Book", {
id: types.identifier(),
name: types.string,
pages: types.array(Page)
}).static({
async fetchSnapshot(api: apiClient, bookId: string) {
const {pagesIds, book} = await api.getBook(bookId);

return {
  ...book,
  pages: await Promise.all(pagesIds.map(pageId => Book.fetchSnapshot(api, pageId)))
}

}
});

My immediate usecase is for data fetching and getting the initial snapshot
of a model.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/mobxjs/mobx-state-tree/issues/584, or mute the thread
https://github.com/notifications/unsubscribe-auth/ABvGhD-QT1VR5EpGK1N__Yr1XUkEq-Miks5tHwb-gaJpZM4RVNHX
.

In Js yes, with TS you will get an error :)

Yeah, you need to tell TS about your additional method :)

const TmpBook = types.model("Book", {
  id: types.identifier(),
  name: types.string,
  pages: types.array(Page)
}).static({
  async fetchSnapshot(api: apiClient, bookId: string) {
    const {pagesIds, book} = await api.getBook(bookId);

    return {
      ...book,
      pages: await Promise.all(pagesIds.map(pageId => Book.fetchSnapshot(api, pageId)))
    }
  }
});

(TmpBook as any).fetchSnapshot = function(){}

export type IBookStatics = {
  fetchSnapshot(): any
}
export const Book: typeof TmpBook & IBookStatics = TmpBook as any

@mattiamanzati its still a feature request,
So i guess its "won't implement?"? :)

I think we could implement it. Would there be downsides in doing it? I can only think two

  1. increase of api surface
  2. doesn't work nice with composition? E.g const A = types.model(...).static(...); const B = A.actions(...). Will B have A's statics? If not, they would be very easily lost, so I think the should be preserved through type extensions, but would there be drawbacks in that?

I want to point out that Backbone.extend has ability to add statics or "class properties "
http://backbonejs.org/#Model-extend

And my implementation for this feature as an external function:

export function MSTAddStatics<T extends IModelType<any, any>, TStatics extends object>(m: T, statics: TStatics) {
  Object.keys(statics).forEach(k => ((m as any)[k] = (statics as any)[k]));
  return m as T & TStatics;
}

I usually tend to avoid to use statics, I use repository classes instances instead. As your example, instead of having static fetchSnapshot on the Book, I have a BookRepository class, and a global singleton instance of that. The BookRepository has the wanted method, and can be inherited and composed easily as it would do for other models, and if you have some Dependency Injection library, you could also easily swap it while running the test suite :)

I have a good use case for that, to add another abstraction layer over fetching and creating models that requires async loading of initial snapshot,
I will close this issue and hopefully will be back with solid examples

no news here?

@francesco-carrella see my comment: https://github.com/mobxjs/mobx-state-tree/issues/584#issuecomment-355977290

thanks @Bnaya, is looks nice but I suppose it keeps out all non-TS cases.
What about adding the .static() method as you mentioned here? https://github.com/mobxjs/mobx-state-tree/issues/584#issue-286461578

I want to point out that Backbone.extend has ability to add statics or "class properties "
http://backbonejs.org/#Model-extend

And my implementation for this feature as an external function:

export function MSTAddStatics<T extends IModelType<any, any>, TStatics extends object>(m: T, statics: TStatics) {
  Object.keys(statics).forEach(k => ((m as any)[k] = (statics as any)[k]));
  return m as T & TStatics;
}

Underrated comment and snippet. Thanks for the inspiration, @Bnaya !!

Do you folks get SomeType implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer when using something like the MSTAddStatics function or a plain old Object.assign when defining the type? I am finding it tricky to avoid!

@airhorns using explicit return types on your views and actions often helps with that. For further details see the TypeScript docs or open a new issue, since this one is closed and most won't see your comment

Was this page helpful?
0 / 5 - 0 ratings