Comments (14)
Your statement
I would expect that if we were to set a value inside of object this value would propagate under all circumstances
into the store, but there is one where it doesn't.
holds true only within reactive context.
Let me explain
Outside reactive context the values don't "propagate" on change, might seem like it, but it is just reading what reference points to, e.g.
let object = { prop: "value" };
function logProperty() {
console.log(object.prop);
}
logProperty(); // logs "value"
object.prop = "new value";
logProperty(); // logs "new value"
Here function logProperty
didn't track changes for object.prop
and didn't get notified that object.prop
has changed, but still logged newest value.
The reactive context allows us to trigger different behaviors when the tracked value changes. This means, that we don't need to manually call logProperty
, we can define behaviour that reacts to changing a value.
const [object, setObject] = createStore({ prop: "value" });
createEffect(function logProperty() {
console.log(object.prop);
}); // logs "value" (in case we wouldn't call setObject immediately after)
setObject("prop", "new value"); // logs "new value"
Here the createEffect
does track changes for object.prop
and gets called when it changes. This is possible, because now object
is defined as a trackable value and through provided interface (setObject
) we notify the effect that we changed the value. This notification wouldn't happen if we didn't use the store interface. I mean the ❌ object.prop = "new value" ❌
wouldn't trigger anything (except maybe eslint 😉, but that's good, because we didn't create trackable to not trigger changes).
Back to your issue
In your example
let object = {};
const [store, setStore] = createStore(object)
setStore('fake item', 'fake id');
object['fake item'] = createUniqueId();
console.log(store["fake item"] === object["fake item"]) // logs false
Changing the property of object
, the object['fake item'] = createUniqueId();
, doesn't trigger anything, because it isn't trackable value. Even though the store
is dependent on it, this change doesn't cause store
to change. It might seem like it changed something after you unwrapped store, but unwrapping returns the reference to original object, so it reads original object at given time. It might seem like it changed something also when you don't unwrap, but at the same time don't use setStore
, because then the store uses direct reference to original object, only after using setStore
it becomes more deeply independent.
Other way around if you used only setStore
and not object['fake item'] = createUniqueId();
, the original object can change (and it does), because store holds reference to original object and it can change its' properties.
let object = {};
const [store, setStore] = createStore(object)
setStore('fake item', 'fake id');
console.log(store["fake item"] === object["fake item"]) // logs true
Then also the properties of unwrapped store wouldn't differ from properties of store, because with setStore
you change original object and the store.
Changing only original object doesn't change the store.
Solution
I am not sure why you need to control original object outside store AND the store separately, and also not sure why you need reactivity here (your sandbox example doesn't show anything reactive), so
- if you don't need to control original object outside store, then you don't need to hold it
const [store, setStore] = createStore({}); setStore("prop", 123); unwrap(store); // when you need to read original for some reason
- if you don't need reactivity, you can just use plain object
let object = {}; object.prop = 123;
- if you need to control moreless same object with 2 interfaces with reactivity, you can define original object as trackable (I would treat it as a fun fact, I think this always can be restructured to one of the solutions above)
const [object, setObject] = createStore({}); const [store, setStore] = createStore(object); setStore("prop", 123); // changes object and store setObject("prop", 123); // changes object and store
from solid.
And for the other issue do you mean something like this?
https://playground.solidjs.com/anonymous/4d348a1d-5ee3-45d4-90f6-c9bc72c35e5f
createComputed sets component color when instance color changes, but when component color changes instance color stays the sameYeah that could also be done through an effect, the thing is that this gets really unmaintanable once the objects get big and there are many objects. But I actually meant for it to be done the other way around, so the instance changes when the component changes. But also, if the instance changes and then the component changes the instance shouldn't change anymore for only the changed property.
Well it does get harder to maintain if you need to take care of each property separately, but there's always a way if you want to treat all of them the same way, so something like iterating through all properties should do the work. Also there's something like createReaction that can trigger only once dependency changes.
There are actually much more handy utilities in docs, take your time to get to know them. Even though your case is VERY specific it's possible to do. I managed to do something you described, but there are many ways to implement this. Here's one example
https://playground.solidjs.com/anonymous/494d6068-6e82-4615-81c0-db40ebe9b49d
You can change instance as many times as you want, but once you change component, you can change instance only with component.
Back to main topic, what you described originally is probably not a bug. If you have problems with understanding library or need some tips official discord might be a better place for this disccusion.
from solid.
Feeling like this might be the culprit:
solid/packages/solid/store/src/store.ts
Lines 209 to 238 in 8de75a4
from solid.
Hey @maciek50322, thanks for taking the time to look into this!
Changing the property of object, the object['fake item'] = createUniqueId();, doesn't trigger anything, because it isn't trackable value. Even though the store is dependent on it, this change doesn't cause store to change. It might seem like it changed something after you unwrapped store, but unwrapping returns the reference to original object, so it reads original object at given time. It might seem like it changed something also when you don't unwrap, but at the same time don't use setStore, because then the store uses direct reference to original object, only after using setStore it becomes more deeply independent.
I don't exactly want it to trigger anything, the way I have things setup is that I want to defineProperty
into the target object with a getter
that returns a reactive value (i.e. a signal or a store), so that it would be reactive to that value, kind of like what Solid already does from your doubly wrapped store solution. The problem
is that even with the defineProperty
in the original object it won't update, but only after the set on the store.
So, this example of me setting just the value in the object is just to get a more minimal reproduction, not necessarily my use case.
Changing only original object doesn't change the store.
I've noticed it hasn't but it only does in this case, the thing is that this behavior doesn't seem to be necessarily intended, just what is right now as it's never been an issue, so
my thought is that this could be probably circumvented here in the code. Thinking here that, as the set
for the store
will change the target object, it could also keep the state of the object in sync if necessary.
From your solution:
if you need to control moreless same object with 2 interfaces with reactivity, you can define original object as trackable (I would treat it as a fun fact, I think this always can be restructured to one of the solutions above)
The problem with that is that the getters from the original store can't really be reset back again. Only if I were able to Object.defineProperty into the store again then it would work. My use case demands that I'm able to have the value
and then go back to the getter if I intend to switching back-and-forth.
A solution would probably work completely if I could programmatically add a getter into the store, I could just set the value,
but I do need a getter, so not sure if something like the following would work.
const [store, setStore] = createStore({ });
setStore('key', '1234');
setStore(produce(value => Object.defineProperty(value, 'key', { get: () => {...}, enumerable: true, configurable: true; })
This illustrates a bit more on my problem here as AFAIK I can only set this getter programmatically in the original object,
and if I do on the store not sure how the Proxy wrapped object would behave.
from solid.
Another problem with the last solution you provided is that both of them are in sync, whereas I want only the second store to be in sync with the first while the first is independent of the second. Does that make sense? Just a one-way data flow so that things are immutable if I need to, and mutable, by only changing, on the second store if I need to as well.
from solid.
I'm still a long way from understanding the code for the setter in the store, but as far as I understand, once I set the value Solid starts (no pun intended) returning the value of a signal on the property's getter, and then, that would be the reason the signal gets out of sync with the original object's property. The only reason that the target object updates when I do set
is that the target object is in sync after only being set to the store is because Solid also updates it when setting.
This would be fine if I could either, define a getter programatically, or delete the signal manaully so that it just returns the value of the target when I know I need to. These would also probably be better solutions than what I'm trying right now.
from solid.
Another problem with the last solution you provided is that both of them are in sync, whereas I want only the second store to be in sync with the first while the first is independent of the second. Does that make sense? Just a one-way data flow so that things are immutable if I need to, and mutable, by only changing, on the second store if I need to as well.
@maciek50322 I also made a playground with a minimal version of my use case if you want to try out any solutions
https://playground.solidjs.com/anonymous/97674bdd-bcfe-49de-8cf5-83144de079fb
Thanks again for taking the time to help me out on this :)
from solid.
This would be fine if I could either, define a getter programmatically
Tried many things for this, but none of them worked. Here are two of them:
-
const [signal, setSignal] = createSignal('this is not John'); const [store, setStore] = createStore({}); setStore('name', 'John'); Object.defineProperty(store, 'name', { get() { return signal(); }, configurable: true, enumerable: true }); expect(store.name).toBe('this is not John') // fails
-
const [signal, setSignal] = createSignal('this is not John'); const [store, setStore] = createStore({}); setStore('name', 'John'); setStore(reconcile({ get name() { return signal(); } }, { merge: true })); expect(store.name).toBe('this is not John') // seems to work setSignal("maybe it is John"); expect(store.name).toBe('maybe it is John') // fails
from solid.
Yeah so custom getter usually will get evaluated to assign the value to store property, but I don't think you need to define custom getter if you want to just pass signal as property. If you pass signal as property, you pass accessor, which return newest value.
const [signal, setSignal] = createSignal("this is not John");
const [store, setStore] = createStore({ name: signal });
console.log(store.name()); // this is not John
setSignal("another name");
console.log(store.name()); // another name
Small caveat there if you want to change store property to different signal you do
setStore("name", () => signal)
And for the other issue do you mean something like this?
https://playground.solidjs.com/anonymous/4d348a1d-5ee3-45d4-90f6-c9bc72c35e5f
createComputed
sets component
color when instance
color changes, but when component
color changes instance
color stays the same
from solid.
And for the other issue do you mean something like this?
https://playground.solidjs.com/anonymous/4d348a1d-5ee3-45d4-90f6-c9bc72c35e5f
createComputed sets component color when instance color changes, but when component color changes instance color stays the same
Yeah that could also be done through an effect, the thing is that this gets really unmaintanable once the objects get big and there are many objects. But I actually meant for it to be done the other way around, so the instance changes when the component changes. But also, if the instance changes and then the component changes the instance shouldn't change anymore for only the changed property.
from solid.
Back to main topic, what you described originally is probably not a bug. If you have problems with understanding library or need some tips official discord might be a better place for this disccusion.
I'm not sure though, might be something to consider about the behavior of createStore
. Regardless I think I'm going to end up doing something like someone from Discord described inside of a primitive for this, might end up being better.
I'll leave this open for now as Ryan might consider it a bug.
from solid.
Another thing I experimented with is patching Solid to export the underlying $NODE symbol so as to delete the signal for the property that is being set again. This actually is feasible for my use case, the primitive ended up being too complex to write but it is possible still.
from solid.
If anyone is interested in a solution here, I ended up using a proxy OVER a store that contains the overwritten values,
then I only override the getters and the set
for Solid stays the same along with all its behavior. Could not do it without this awesome library
from solid.
Nice. Yeah these sort of considerations are something that I do pay attention to. I will say the current behavior is very much by design and would be difficult to change in a performant way. It is sort of the nature of the beast but understanding the intent and effort that goes into working around this for specific cases is incredibly invaluable. Thank you posting your efforts on our github so that we have a record of it.
from solid.
Related Issues (20)
- Cannot use istanbul ignore comments for generated code HOT 2
- setting element reference in `setStore` modifies the values in the array HOT 13
- `this` inside jsx-template inside a constructor of a derived class causes transformation-bug HOT 2
- The update of props is not granular enough. HOT 4
- No value attribute on CSR, but present in SSR
- Cannot properly bind numbers to Select HOT 1
- Double mounting for children element. HOT 2
- Is [email protected] active now? HOT 2
- Enhancement - Prop destructuring HOT 4
- [SSR/Astro] DOM Not Updated from State by Client Updates onMount HOT 5
- Boolean attributes HOT 1
- Component doesn't reliably respond to a signal after hydration HOT 3
- JSX does not wait for transitions to complete when using a ternary in a prop value HOT 4
- What is the minimum browser support for SolidJS? HOT 2
- TypeScript doesn't give error when calling a SetStoreFunction<T> where T is an array of objects HOT 13
- SyntaxError: The requested module 'node:events' does not provide an export named 'addAbortListener' HOT 4
- When `createResource` is used to load entities, and `createStore` is used to reference those entities, updates to store properties which reference entities should not mutate the original resource.
- Attempted import error HOT 1
- Using enableScheduling in a simple solidjs-router project throws errors on transitions HOT 1
- [Bug?]: <noscript> content renders on the client HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from solid.