Tram-One / tram-one

🚋 Legacy View Framework For Vanilla Javascript
http://tram-one.io/
MIT License
36 stars 8 forks source link

Difficult to copy over parts of an observable #182

Open JRJurman opened 2 years ago

JRJurman commented 2 years ago

Summary

As a side-effect of how stores are made (proxy objects), when you set a property of a store to another store's property (for example, when managing a saved state), you get the reference to the other store, not it's value:

const workingStore = useStore({ numbers: [1,2,3,4] });
const savedStore = useStore({ numbers: [1,2,3] });

const onSave = () => {
  // doesn't just update the value, sets the proxy pointer!
  savedStore.numbers = workingStore.numbers
}

This can be worked around by either of the following ways:

  1. deconstruct the value into raw data, e.g. using [...workingStore.numbers]
  2. expose the raw() function that the observable library provides

The first solution can be cumbersome an error prone, as the store structure gets larger or more complicated.

The second solution could lead to misuse / confusion on how stores work - up until this point they are treated as black-box objects that "just work", exposing a raw() function could lead to people interacting with that more than they should, in ways they really shouldn't / don't need to.

Conversely, exposing the raw() function could also be really really useful for debugging purposes, right now it is somewhat non-trivial to inspect a the proxy-observable store, but this would make it much easier.

JRJurman commented 2 years ago

If we do decide to go with the "exposing the raw() function" plan, we could choose to make it part of the interface for the stores, or part of the Tram-One library.

The most trivial way to implement this would be through Tram-One imports:

import { useStore, raw } from 'tram-one'
const useNumbers = () => {
  const workingStore = useStore({ numbers: [1,2,3,4] });
  const savedStore = useStore({ numbers: [1,2,3] });

  const onSave = () => {
    savedStore.numbers = raw(workingStore).numbers
  }
}

But we could add a raw method to observables that just expose the raw values:

import { useStore } from 'tram-one'
const useNumbers = () => {
  const workingStore = useStore({ numbers: [1,2,3,4] });
  const savedStore = useStore({ numbers: [1,2,3] });

  const onSave = () => {
    savedStore.numbers = workingStore.raw().numbers
  }
}
JRJurman commented 2 years ago

Conversely, we could expose an interface that is specifically for "copying over parts-of or entire stores to other stores". This could allow us to deliver a purposeful copy() method that uses the internal raw() method, without exposing it directly. That doesn't help the debugging case (of course we could make a debug() method too, but at that point, I think we admit the versatility of raw() is worthwhile).

JRJurman commented 2 years ago

This branch exposes a raw() method for people to import. I'm still not 100% sure this is something we want to expose - many times I've wanted to use this, it turned out to be an inappropriate solution :thinking:

https://github.com/Tram-One/tram-one/compare/raw-export