Nozbe / WatermelonDB

🍉 Reactive & asynchronous database for powerful React and React Native apps ⚡️
https://watermelondb.dev
MIT License
10.62k stars 600 forks source link

Unable to persist create records using Loki as adapter #207

Closed bgits closed 5 years ago

bgits commented 5 years ago

I have added watermelondb + Loki to a project and when creating records the raw values are all the default values of empty strings and 0 numbers. When I log out the records during the batch creation they are there but upon retrieval the raw values are the defaults.

When writing directly to the raw field it does persist, ie: lpEvent._raw.block_number

Here is the actual create action in the repo branch: https://github.com/status-im/liquid-funding/blob/watermelon-data-persist/app/actions/lpEvents.js#L19

radex commented 5 years ago

@bgits You probably have ES6 decorators misconfigured. That's why writes to model.xx never actually writes to model._raw as it should

bgits commented 5 years ago

@bgits You probably have ES6 decorators misconfigured. That's why writes to model.xx never actually writes to model._raw as it should

The configuration in webpack is here, it seems to be correct and the build does not error. https://github.com/status-im/liquid-funding/blob/watermelon-data-persist/webpack.config.js#L129-L133

How would you test to see if the decorators are properly configured?

radex commented 5 years ago

@bgits look at compiled JS files — do they look right?. Also, I remember someone else had a similar issue, so you can browse closed issues in this repo

bgits commented 5 years ago

@bgits look at compiled JS files — do they look right?. Also, I remember someone else had a similar issue, so you can browse closed issues in this repo

Is this the issue you are referring to? https://github.com/Nozbe/WatermelonDB/issues/104

In my case the legacy field is set to true. Setting legacy: true requires class-properties to have loose: true. Does that work well with WatermelonDB?

bgits commented 5 years ago

With no clear differences in my babel config, I for now resorted to defining fields in model constructor using this helper function:

 const fieldGenerator = self => (column, name) => {
  Object.defineProperty(self, name || column, {
    get() { return self._getRaw(column) },
    set(value) { self._setRaw(column, value) },
    enumerable: true,
    configurable: true
  })
}

Then using it as:

export default class LpEvent extends Model {
  constructor(...args) {
    super(...args)
    const field = fieldGenerator(this)
    field('event_id', 'eventId')
    field('address')
    field('event')
    field('block_number', 'blockNumber')
  }

  static table = 'lp_events'

This is a hacky solution and will not be easy when I need to start having relationships, so I'm wondering if there is a recommendation for use the decorators as functions? As decorator support is not widespread yet being forced to use them is currently the largest detractor.

radex commented 5 years ago

Is this the issue you are referring to? #104

Indeed! @alex-min — can you share what you had misconfigured in babelrc?

Setting legacy: true requires class-properties to have loose: true. Does that work well with WatermelonDB?

Yes! That's what I have in my config.

This is a hacky solution and will not be easy when I need to start having relationships, so I'm wondering if there is a recommendation for use the decorators as functions? As decorator support is not widespread yet being forced to use them is currently the largest detractor.

Agreed — decorators are neat, but shouldn't be mandatory. I'm not going to work on this, but if you want to contribute a solution, I'd be happy to accept it.

alex-min commented 5 years ago

@radex

It seems I had something like this:

{
  "presets": ["module:metro-react-native-babel-preset"],
  "plugins": [
    ["@babel/plugin-transform-flow-strip-types"],
    ["@babel/plugin-proposal-decorators", { "legacy": true }]
  ]
}

instead of

{
  "presets": ["module:metro-react-native-babel-preset"],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    ["@babel/plugin-transform-runtime", {
      "helpers": true,
      "regenerator": false
    }]
  ]
}

I don't remember how I got that however...

bgits commented 5 years ago

It seems using a decorator on a class property is simply ignored by babel for some reason.

I got it to work using this approach:

function initialize(target, name, descriptor) {
  descriptor.initializer = true
}

export default class LpEvent extends Model {

  static table = 'lp_events'

  @field('address')
  @initialize
  address() {}
}

By declaring it as a method babel no longer discards it, and I had to create the initialize decorator in order to get around this invariant: https://github.com/Nozbe/WatermelonDB/blob/master/src/decorators/common.js#L21-L25

Which leads me to ask, why is that invariant needed?

radex commented 5 years ago

Which leads me to ask, why is that invariant needed?

It's there to protect users from incorrect use of decorators. You're only supposed to say @field('a') a, not, for example, a method or a setter (since that doesn't really make sense).

Without seeing what you're seeing, it's hard for me to help you diagnose the root cause of the problem — but it would be great to fix it (or at least provide an alternative to decorators), since you're not the first person to have problems setting those up

bgits commented 5 years ago

I believe I found the issue. I think it's related to: https://github.com/babel/babel/issues/8417

Here is the fix in my webpack config: https://github.com/status-im/liquid-funding/commit/2bc3e5fd3426bd91ee8d70e2d43b9ce11e370299