ITADN

Hooks - an outrageous observation

#23Closedclragon 创建于 2025-04-13
C
clragoncommented
I am going to sound like a complete lunatic, but here it goes: I was crafting a custom "useX" query hook with the package `cached_query`, but it hit me: 1. The created query must be stored 2. The dependencies for that query arent obvious to the caller Therefore: 3. The query creation function must store the query To illustrate the problem: ```dart Query<Profile> useProfile(BuildContext context, String id) { final database = DatabaseRef.of(context); return Query( key: ['user_profile', id], queryFn: () => ..., ); } ``` Our dependency in this example here is `database` and `id`. The query may depend on any number of things in the BuildContext, which the caller doesnt have to know about. But if the caller must store the query, they must know the dependencies. Because of that, the caller cannot store the query. This hook has to store it. I am trying to stay within context_plus for state management for this project, so the obvious next question is: How can I use context_plus to dynamically store state only for the widget I am calling from? If everything was known ahead of time, this would be easy. I can create a private Ref inside the Widget or its File and use it to create and manage the lifecycle of the query. But this function cannot do that. A static private Ref that is shipped with this function would have the immediate issue of propagating through the tree. A desirable property usually, but not in this case. We want this query to remain private unless otherwise specified. This all is to say: We want to "abuse" context_plus to dynamically store state based on the widget we are in. So, can we? **No.** Unfortunately, it turns out, a Ref is not simply a key to access a value from the surrounding ContextRoot. One might naively assume the following code could work: ```dart final class KeyedRef<T> extends Ref<T> { KeyedRef(this.key); final Object key; @override bool operator ==(Object other) => other.runtimeType == runtimeType && other is KeyedRef<T> && other.key == key; // Could be deep collection equality @override int get hashCode => Object.hash(runtimeType, key); } ``` Since any Ref created with the same key would therefore lead to accessing the same value. This would be similar to a `ValueKey`. However, `InternalReadOnlyRefAPI` follows a Object orientated approach, where its instance stores dependents and providers, rather than simply being a key for a map containing these. A simple test for this can be constructed as such: ```dart Object useObject(BuildContext context) { final bindRef = KeyedRef<Object>(context); bindRef.bind(context, () => Object()); final accessRef = KeyedRef<Object>(context); return accessRef.of(context); } ``` Were this API designed differently, it could be possible to create a hook-like API that stores State for only the calling BuildContext. For example: ```dart Query<Profile> useProfile(BuildContext context, String id) { final database = DatabaseRef.of(context); return KeyedRef<Query>([context, 'use_profile']).bind( context, () => Query( key: ['user_profile', id], queryFn: () => ..., ), key: [database, id], ); } ``` That is all of my findings. I dont have a specific conclusion to draw from this. Supporting hook-like constructs in this package would be rather fascinating, but at the same time could exceed the scope by miles, and would most likely require completely rethinking how Refs work internally. It might be tempting to include such a thing to lean into the "replacement of statefulwidgets", which this could enable at a composable scale.
关闭于 2025-04-19 12 条评论