hybridsjs/hybrids

Properties assigned undefined when accessing them for the first time

rubixibuc opened this issue · 1 comments

Hi!

I'm creating a property definition similarly matching the following

localStorageBackProperty: {
  get: () =>
    localStorage.getItem("localStorageBackProperty") &&
    new Date(localStorage.getItem("localStorageBackProperty")),
  set: (host, value) => {
    localStorage.setItem("localStorageBackProperty", value + "")
  }
}

What I'm seeing is that simply reading the variable host.localStorageBackProperty is for some reason passing undefined to the property setter, erasing the value that was in localStorage

Is this expected?

hybrids version: 7.1.0

The get and set method of the property definition works similarly to the getter/setter from the Object.defineProperty() function.

The set method is additionally called, if the value is not defined, as you may want to compute the value only by defining the set method - https://github.com/hybridsjs/hybrids/blob/v7.1.0/src/define.js#L179 - without defining the get - then the set is called also for the first access of the property.

In simpler words, the set method should always return a value, which is passed to get, as a lastValue when property is accessed. Your code example does not return a value in set method.

However, in general, you should use store factory in your case, as you try to combine an external source with a simple get / set method - in which a source is an internal cache of the component itself.

It would not be that complicated:

import { store } from "hybrids";

const Data = {
  back: "",
  [store.connect]: {
    get: () => JSON.parse(localStorage.getItem("myData")) || {},
    set: (_, data) => {
      localStorage.setItem("myData", JSON.stringify(data));
      return data;
    },
  }
};

// component
{
  data: store(Data),
  // or
  back: () => store.get(Data).back,
}

// updating the value
store.set(data, { back: ... });

The above example uses a dictionary of keys, rather then a single value, but it is easy to refactor it to work that way.

You can also take out the [store.connect] definition to the storage factory function, like [store.connect]: localStorage("myKey") if there are more models like this, and use it:

const Options = {
  ...,
  [store.connect]: localStorage("options"),
};