Skip to content

Commit 71245c4

Browse files
authored
Merge pull request #3039 from unoplatform/dev/jela/mvux-hr
fix(feeds): Don't use delegates as key for instance caching
2 parents 85da926 + 46a18ab commit 71245c4

File tree

2 files changed

+28
-12
lines changed

2 files changed

+28
-12
lines changed

src/Uno.Extensions.Reactive/Core/ListState.T.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,23 +64,36 @@ public static IListState<T> Empty<TOwner>(TOwner owner, [CallerMemberName] strin
6464
/// <typeparam name="TOwner">Type of the owner of the state.</typeparam>
6565
/// <param name="owner">The owner of the state.</param>
6666
/// <param name="valueProvider">The provider of the initial value of the state.</param>
67+
/// <param name="name">The caller member name, used as a stable cache key.</param>
68+
/// <param name="line">The caller line number, used as a stable cache key.</param>
6769
/// <returns>A feed that encapsulate the source.</returns>
68-
public static IListState<T> Value<TOwner>(TOwner owner, Func<IImmutableList<T>> valueProvider)
70+
public static IListState<T> Value<TOwner>(TOwner owner, Func<IImmutableList<T>> valueProvider, [CallerMemberName] string? name = null, [CallerLineNumber] int line = -1)
6971
where TOwner : class
70-
// Note: We force the usage of delegate so 2 properties which are doing State.Value(this, () => 42) will effectively have 2 distinct states.
71-
=> AttachedProperty.GetOrCreate(owner, valueProvider, static (o, v) => SourceContext.GetOrCreate(o).CreateListState(Option<IImmutableList<T>>.Some(v())));
72+
// Use CallerMemberName+line as stable cache key instead of delegate reference identity.
73+
// Delegate instances can be recreated after MetadataUpdater.ApplyUpdate on WASM,
74+
// which would cause cache misses and state recreation (spec 033).
75+
=> AttachedProperty.GetOrCreate<TOwner, (string, int), Func<IImmutableList<T>>, IListState<T>>(
76+
owner,
77+
(name ?? throw new InvalidOperationException("The name of the list state must not be null"), line < 0 ? throw new InvalidOperationException("The provided line number is invalid.") : line),
78+
valueProvider,
79+
static (o, _, v) => SourceContext.GetOrCreate(o).CreateListState(Option<IImmutableList<T>>.Some(v())));
7280

7381
/// <summary>
7482
/// Gets or creates a list state from a static initial list of items.
7583
/// </summary>
7684
/// <typeparam name="TOwner">Type of the owner of the state.</typeparam>
7785
/// <param name="owner">The owner of the state.</param>
7886
/// <param name="valueProvider">The provider of the initial value of the state.</param>
87+
/// <param name="name">The caller member name, used as a stable cache key.</param>
88+
/// <param name="line">The caller line number, used as a stable cache key.</param>
7989
/// <returns>A feed that encapsulate the source.</returns>
80-
public static IListState<T> Value<TOwner>(TOwner owner, Func<ImmutableList<T>> valueProvider)
90+
public static IListState<T> Value<TOwner>(TOwner owner, Func<ImmutableList<T>> valueProvider, [CallerMemberName] string? name = null, [CallerLineNumber] int line = -1)
8191
where TOwner : class
82-
// Note: We force the usage of delegate so 2 properties which are doing State.Value(this, () => 42) will effectively have 2 distinct states.
83-
=> AttachedProperty.GetOrCreate(owner, valueProvider, static (o, v) => SourceContext.GetOrCreate(o).CreateListState(Option<IImmutableList<T>>.Some(v())));
92+
=> AttachedProperty.GetOrCreate<TOwner, (string, int), Func<ImmutableList<T>>, IListState<T>>(
93+
owner,
94+
(name ?? throw new InvalidOperationException("The name of the list state must not be null"), line < 0 ? throw new InvalidOperationException("The provided line number is invalid.") : line),
95+
valueProvider,
96+
static (o, _, v) => SourceContext.GetOrCreate(o).CreateListState(Option<IImmutableList<T>>.Some(v())));
8497

8598
/// <summary>
8699
/// Gets or creates a list state from a static initial list of items.

src/Uno.Extensions.Reactive/Core/ListState.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Collections.Immutable;
44
using System.Linq;
5+
using System.Runtime.CompilerServices;
56
using System.Threading;
67

78
namespace Uno.Extensions.Reactive;
@@ -34,11 +35,12 @@ public static IListState<TValue> Create<TOwner, TValue>(TOwner owner, Func<Cance
3435
/// <typeparam name="TValue">The type of the value of the resulting feed.</typeparam>
3536
/// <param name="owner">The owner of the state.</param>
3637
/// <param name="valueProvider">The provider of the initial value of the state.</param>
38+
/// <param name="name">The caller member name, used as a stable cache key.</param>
39+
/// <param name="line">The caller line number, used as a stable cache key.</param>
3740
/// <returns>A state that encapsulate the source.</returns>
38-
public static IListState<TValue> Value<TOwner, TValue>(TOwner owner, Func<IImmutableList<TValue>> valueProvider)
41+
public static IListState<TValue> Value<TOwner, TValue>(TOwner owner, Func<IImmutableList<TValue>> valueProvider, [CallerMemberName] string? name = null, [CallerLineNumber] int line = -1)
3942
where TOwner : class
40-
// Note: We force the usage of delegate so 2 properties which are doing State.Value(this, () => 42) will effectively have 2 distinct states.
41-
=> ListState<TValue>.Value(owner, valueProvider);
43+
=> ListState<TValue>.Value(owner, valueProvider, name, line);
4244

4345
/// <summary>
4446
/// Gets or creates a list state from a static initial list of items.
@@ -47,11 +49,12 @@ public static IListState<TValue> Value<TOwner, TValue>(TOwner owner, Func<IImmut
4749
/// <typeparam name="TValue">The type of the value of the resulting feed.</typeparam>
4850
/// <param name="owner">The owner of the state.</param>
4951
/// <param name="valueProvider">The provider of the initial value of the state.</param>
52+
/// <param name="name">The caller member name, used as a stable cache key.</param>
53+
/// <param name="line">The caller line number, used as a stable cache key.</param>
5054
/// <returns>A state that encapsulate the source.</returns>
51-
public static IListState<TValue> Value<TOwner, TValue>(TOwner owner, Func<ImmutableList<TValue>> valueProvider)
55+
public static IListState<TValue> Value<TOwner, TValue>(TOwner owner, Func<ImmutableList<TValue>> valueProvider, [CallerMemberName] string? name = null, [CallerLineNumber] int line = -1)
5256
where TOwner : class
53-
// Note: We force the usage of delegate so 2 properties which are doing State.Value(this, () => 42) will effectively have 2 distinct states.
54-
=> ListState<TValue>.Value(owner, valueProvider);
57+
=> ListState<TValue>.Value(owner, valueProvider, name, line);
5558

5659
/// <summary>
5760
/// Gets or creates a list state from an async method.

0 commit comments

Comments
 (0)