mobx-state-tree icon indicating copy to clipboard operation
mobx-state-tree copied to clipboard

Get snapshot of instance of custom type

Open gustavohenke opened this issue 3 years ago • 3 comments

Feature request

Is your feature request related to a problem? Please describe.

tl;dr: It seems not possible to get a snapshot of an instance of a custom type without using a model.

Suppose that in a todo app, each todo can have a date it's due by. Since the user can change timezones, it'd be gross for the app to notify them of the due work too early/late, so the time isn't stored. In the server, this would be suitable to save as a string in the format YYYY-MM-DD.

In the client though, since it's easier to deal with Date objects for comparing and rendering, it'd be natural to have a custom MST type that gets used by the Todo model:

const CustomDate = types.custom<string, Date>({
	name: "CustomDate",
	fromSnapshot: value => parseCustomDate(value),
	toSnapshot: value => formatCustomDate(value),
	// ...
})

Since the application is big, it's natural to keep a layer exclusive to data fetching, which holds no state. For this, we can have the TodoFetcher model, which must fetch todos due by a given date. It queries the server in its original format YYYY-MM-DD.

types.model('TodoFetcher', { ... }).actions(() => ({
	fetchTodos(date: Instance<CustomDate>) {
		// ...
	}
});

However, date is a standalone value. It doesn't belong to a model, so there's no good way to serialise it before sending the HTTP request. The API of a custom type like CustomDate only offers ways to create instances and validate values, and no ways to run the logic in toSnapshot:

Screen Shot 2022-01-24 at 02 02 21

I take that I might not have researched enough, or that I'm just too new to MST to fully understand how things are done

Describe the solution you'd like

The addition of a method to custom types that can run its snapshotting logic when there's a standalone instance value.

Describe alternatives you've considered

  1. Making a model that holds the single CustomDate value above so that I can getSnapshot() it
  2. Duplicate the logic in CustomDate#toSnapshot() around the codebase
  3. Using a more complicated model that stores both server and client representations with some custom snapshot processing logic
  4. Setting the date in the state of TodoFetcher, then calling an argument-less fetchTodos

Additional context

Disclaimer: I'm not building a todo app; this is a translation of the problem from my business logic to something every reader will understand. The same problems still apply.

Are you willing to (attempt) a PR?

  • [x] Yes
  • [ ] No

gustavohenke avatar Jan 23 '22 15:01 gustavohenke

I think to and fromsnapshot are available on custom types. https://mobx-state-tree.js.org/API/#custom

I’m on my phone, so I may have missed the major thrust of your issue. I apologize if that’s the case.

On Sun, Jan 23, 2022 at 9:17 AM Gustavo Henke @.***> wrote:

Feature request

Is your feature request related to a problem? Please describe.

tl;dr: It seems not possible to get a snapshot of an instance of a custom type without using a model.

Suppose that in a todo app, each todo can have a date it's due by. Since the user can change timezones, it'd be gross for the app to notify them of the due work too early/late, so the time isn't stored. In the server, this would be suitable to save as a string in the format YYYY-MM-DD.

In the client though, since it's easier to deal with Date objects for comparing and rendering, it'd be natural to have a custom MST type that gets used by the Todo model:

const CustomDate = types.custom<string, Date>({ name: "CustomDate", fromSnapshot: value => parseCustomDate(value), toSnapshot: value => formatCustomDate(value), // ...})

Since the application is big, it's natural to keep a layer exclusive to data fetching, which holds no state. For this, we can have the TodoFetcher model, which must fetch todos due by a given date. It queries the server in its original format YYYY-MM-DD.

types.model('TodoFetcher', { ... }).actions(() => ({ fetchTodos(date: Instance<CustomDate>) { // ... }});

However, date is a standalone value. It doesn't belong to a model, so there's no good way to serialise it before sending the HTTP request. The API of a custom type like CustomDate only offers ways to create instances and validate values, and no ways to run the logic in toSnapshot:

[image: Screen Shot 2022-01-24 at 02 02 21] https://user-images.githubusercontent.com/826553/150684766-73fdf926-5bdf-4347-9f97-f1316ecb0409.png

I take that I might not have researched enough, or that I'm just too new to MST to fully understand how things are done

Describe the solution you'd like

The addition of a method to custom types that can run its snapshotting logic when there's a standalone instance value.

Describe alternatives you've considered

  1. Making a model that holds the single CustomDate value above so that I can getSnapshot() it
  2. Duplicate the logic in CustomDate#toSnapshot() around the codebase
  3. Using a more complicated model that stores both server and client representations with some custom snapshot processing logic

Additional context

Are you willing to (attempt) a PR?

  • Yes
  • No

— Reply to this email directly, view it on GitHub https://github.com/mobxjs/mobx-state-tree/issues/1862, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABCW4XKWQQ7ITLS3HDMYV3UXQLXRANCNFSM5MTOUGVQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you are subscribed to this thread.Message ID: @.***>

-- -Matt Ruby- @.***

mattruby avatar Jan 23 '22 18:01 mattruby

hi @mattruby, that's just how the custom type must be implemented. Its returned value doesn't have that.

gustavohenke avatar Jan 24 '22 08:01 gustavohenke

OK, I've re-read your question in more detail. Hmmm. So you'd like to add some kind of static function off the custom type to expose toSnapshot. I think I'd just make that a function that your custom type imports and share it out that way.

I don't think it's worth spinning up mini model instances just to use the date serialization logic.

On Mon, Jan 24, 2022 at 2:15 AM Gustavo Henke @.***> wrote:

hi @mattruby https://github.com/mattruby, that's just how the custom type must be implemented. Its returned value doesn't have that.

— Reply to this email directly, view it on GitHub https://github.com/mobxjs/mobx-state-tree/issues/1862#issuecomment-1019826906, or unsubscribe https://github.com/notifications/unsubscribe-auth/AABCW4TFR4ECDM6KN3Q7XMTUXUDDJANCNFSM5MTOUGVQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

You are receiving this because you were mentioned.Message ID: @.***>

-- -Matt Ruby- @.***

mattruby avatar Jan 25 '22 15:01 mattruby

Hey @gustavohenke and @mattruby - thanks for the good discussion here. I'm in agreement with @mattruby on the path forward here.

I'm going to close this issue since it's been over a year without activity, and I think y'all found the answer. @gustavohenke - if you feel strongly about serializing these custom types, please consider opening up a discussion and we can talk it out a little more.

coolsoftwaretyler avatar Jun 27 '23 02:06 coolsoftwaretyler