-
Notifications
You must be signed in to change notification settings - Fork 125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add createLens
primitive
#452
base: main
Are you sure you want to change the base?
Conversation
|
Source code is inside |
This is interesting, but why not rely on the "native" store scoping ability: const [store, setStore] = createStore([
{
inner: {
innerString: "first",
innerNumber: 0,
},
},
])
const [inner, setInner] = createStore(store[0].inner)
setInner("innerString", "hello") // reflected in both stores as they share the same reference. With stores you can rely on the references being the same, so any mutations to one will be reflected in all the other proxies that wrap the object. I've replaced the const last = path[path.length - 1];
path = path.slice(0, -1);
const storeNode = store[0] instanceof Function ? store[0]() : store[0];
const value = getValueByPath(storeNode as StoreNode, path) as V;
const [get, set] = createStore(value as any);
return [() => get[last], (...args: any[]) => set(last, ...args)]; I don't hate the idea, but I want to know what exactly is the benefit of these primitives, as creating basic lenses is already in reach. |
I had no idea I'm pretty sure that does everything I need it to do unless I'm missing something. |
@thetarnav That |
@thetarnav, that won't work if the "lens" part can have a primitive value, right? At least, Typescript complains about that case, and I can't see how that would work. Here's an example from a codebase I'm working on at the moment: const [editedGraph, setEditedGraph] = createStore<{ graph: Graph | null }>({
graph: null,
}); Here, the I would like to be able to use lenses to hide this implementation detail from consumers. In this situation, doing The getter side is easy most of the time, because you can just wrap the const setter = (...args) => setEditedGraph("graph", ...args); But it's very difficult to type this function properly in such a way that it still behaves exactly like the normal Something like |
Utilities for working with nested reactivity in a modular way.
createLens
- Given a path within a Store object, return a derived or "focused"getter and setter pair.
createFocusedGetter
- The first half of the lens tuple; a derived signalusing path syntax on an object.
createFocusedSetter
- The second half of the lens tuple; a Setterfor a specific path within a Store.
How to use it
Motivation
1. Separation of Concerns
Components can receive scoped Setters for only the parts of state they need
access to, rather than needing a top-level
setStore
function.2. Type-safety
Essentially, we are just partially applying a
setStore
function with an initial path, and returning a function that will apply the
remainder of the path. It is just syntactic sugar, and under the hood
everything is using calls to native Store functionality.
The same approach can already be used by the Setter returned by
createStore
. However,Typescript users will find it hard to maintain type-safety for the arguments
passed to a "derived"/partially-applied Setter. The type definitions for
SetStoreFunction
are...daunting.
The
lenses
package alleviates this friction by providing bothStorePath<T>
and
EvaluatePath<T, P>
generic type helpers.3. Shared path syntax between Getters and Setters
The path syntax defined in Solid Stores is incredibly expressive and powerful.
By introducing
createScopedGetter
, the same syntax can be also be used toaccess Store values as derived Signals. This is particularly relevant to
child components which may both display and modify items from a Store
collection.
Closes #453