Merge pull request #2974 from leptos-rs/more-stores

Improve efficiency of keyed stores
This commit is contained in:
Greg Johnston 2024-09-14 21:49:27 -04:00 committed by GitHub
commit 2bdacf636e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 34 additions and 34 deletions

View file

@ -95,7 +95,10 @@ pub fn App() -> impl IntoView {
<input type="submit"/> <input type="submit"/>
</form> </form>
<ol> <ol>
<For each=move || store.todos().iter_keyed() key=|row| row.id().get() let:todo> // because `todos` is a keyed field, `store.todos()` returns a struct that
// directly implements IntoIterator, so we can use it in <For/> and
// it will manage reactivity for the store fields correctly
<For each=move || store.todos() key=|row| row.id().get() let:todo>
<TodoRow store todo/> <TodoRow store todo/>
</For> </For>
@ -154,17 +157,14 @@ fn TodoRow(
<button on:click=move |_| { <button on:click=move |_| {
let id = todo.id().get(); let id = todo.id().get();
store store.todos().write().retain(|todo| todo.id != id);
.todos()
.update(|todos| {
todos.remove(id);
});
}>"X"</button> }>"X"</button>
<input <input
type="date" type="date"
prop:value=move || { prop:value=move || {
todo.status().scheduled_for_date().map(|n| n.get().to_string()) todo.status().scheduled_for_date().map(|n| n.get().to_string())
} }
class:hidden=move || !todo.status().scheduled_for() class:hidden=move || !todo.status().scheduled_for()
on:change:target=move |ev| { on:change:target=move |ev| {
if let Some(date) = todo.status().scheduled_for_date() { if let Some(date) = todo.status().scheduled_for_date() {

View file

@ -95,13 +95,7 @@ where
{ {
type Value = T; type Value = T;
type Reader = Mapped<Inner::Reader, T>; type Reader = Mapped<Inner::Reader, T>;
type Writer = KeyedSubfieldWriteGuard< type Writer = MappedMut<WriteGuard<ArcTrigger, Inner::Writer>, T>;
Inner,
Prev,
K,
T,
MappedMut<WriteGuard<ArcTrigger, Inner::Writer>, T>,
>;
type UntrackedWriter = type UntrackedWriter =
MappedMut<WriteGuard<ArcTrigger, Inner::UntrackedWriter>, T>; MappedMut<WriteGuard<ArcTrigger, Inner::UntrackedWriter>, T>;
@ -125,11 +119,7 @@ where
let path = self.path().into_iter().collect::<StorePath>(); let path = self.path().into_iter().collect::<StorePath>();
let trigger = self.get_trigger(path.clone()); let trigger = self.get_trigger(path.clone());
let guard = WriteGuard::new(trigger, self.inner.writer()?); let guard = WriteGuard::new(trigger, self.inner.writer()?);
let guard = MappedMut::new(guard, self.read, self.write); Some(MappedMut::new(guard, self.read, self.write))
Some(KeyedSubfieldWriteGuard {
inner: self.clone(),
guard: Some(guard),
})
} }
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> { fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
@ -336,15 +326,21 @@ where
type Value = T; type Value = T;
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> { fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
self.writer() let guard = self.writer()?;
Some(KeyedSubfieldWriteGuard {
inner: self.clone(),
guard: Some(guard),
})
} }
fn try_write_untracked( fn try_write_untracked(
&self, &self,
) -> Option<impl DerefMut<Target = Self::Value>> { ) -> Option<impl DerefMut<Target = Self::Value>> {
self.writer().map(|mut writer| { let mut guard = self.writer()?;
writer.untrack(); guard.untrack();
writer Some(KeyedSubfieldWriteGuard {
inner: self.clone(),
guard: Some(guard),
}) })
} }
} }
@ -642,18 +638,21 @@ where
} }
} }
impl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T> impl<Inner, Prev, K, T> IntoIterator for KeyedSubfield<Inner, Prev, K, T>
where where
Self: Clone, Self: Clone,
for<'a> &'a T: IntoIterator, for<'a> &'a T: IntoIterator,
Inner: StoreField<Value = Prev>, Inner: Clone + StoreField<Value = Prev> + 'static,
Prev: 'static, Prev: 'static,
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static, K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
T: IndexMut<usize>, T: IndexMut<usize> + 'static,
T::Output: Sized, T::Output: Sized,
{ {
type Item = AtKeyed<Inner, Prev, K, T>;
type IntoIter = StoreFieldKeyedIter<Inner, Prev, K, T>;
#[track_caller] #[track_caller]
pub fn iter_keyed(self) -> StoreFieldKeyedIter<Inner, Prev, K, T> { fn into_iter(self) -> StoreFieldKeyedIter<Inner, Prev, K, T> {
// reactively track changes to this field // reactively track changes to this field
let trigger = self.get_trigger(self.path().into_iter().collect()); let trigger = self.get_trigger(self.path().into_iter().collect());
trigger.track(); trigger.track();

View file

@ -60,20 +60,21 @@ impl TriggerMap {
pub struct FieldKeys<K> { pub struct FieldKeys<K> {
spare_keys: Vec<StorePathSegment>, spare_keys: Vec<StorePathSegment>,
current_key: usize, current_key: usize,
keys: HashMap<K, (StorePathSegment, usize)>, keys: FxHashMap<K, (StorePathSegment, usize)>,
} }
impl<K> FieldKeys<K> impl<K> FieldKeys<K>
where where
K: Debug + Hash + PartialEq + Eq, K: Debug + Hash + PartialEq + Eq,
{ {
pub fn new(from_iter: impl IntoIterator<Item = K>) -> Self { pub fn new(from_keys: Vec<K>) -> Self {
let mut current_key = 0; let mut keys = FxHashMap::with_capacity_and_hasher(
let mut keys = HashMap::new(); from_keys.len(),
for (idx, key) in from_iter.into_iter().enumerate() { Default::default(),
let segment = current_key.into(); );
for (idx, key) in from_keys.into_iter().enumerate() {
let segment = idx.into();
keys.insert(key, (segment, idx)); keys.insert(key, (segment, idx));
current_key += 1;
} }
Self { Self {
@ -104,7 +105,7 @@ where
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(idx, key)| (key, idx)) .map(|(idx, key)| (key, idx))
.collect::<HashMap<K, usize>>(); .collect::<FxHashMap<K, usize>>();
// remove old keys and recycle the slots // remove old keys and recycle the slots
self.keys.retain(|key, old_entry| match new_keys.get(key) { self.keys.retain(|key, old_entry| match new_keys.get(key) {