Skip to content

Commit 9dcb51d

Browse files
authored
refactor!: Move component-bound effect to Signal public API (#23566)
* refactor: Move component-bound effect to Signal public API Add Signal.effect(Component, SerializableRunnable) as the public API for creating component-lifecycle-bound signal effects, delegating to the internal Effect.effect(). Rename the existing unbound Signal.effect(EffectAction) to Signal.unboundEffect(EffectAction) to steer users toward the component-bound version. Migrate user-visible code from Effect.effect() to Signal.effect(). * refactor: Remove Effect.effect(Component, ...) in favor of Signal.effect() Signal.effect() now delegates directly to ElementEffect.effect() instead of going through the redundant Effect.effect() intermediary. All callers migrated to the public Signal.effect() API. * fix: Update Javadoc references from Effect#effect to Signal#effect
1 parent 43f4b9f commit 9dcb51d

File tree

17 files changed

+118
-121
lines changed

17 files changed

+118
-121
lines changed

flow-data/src/main/java/com/vaadin/flow/data/binder/Binder.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
import com.vaadin.flow.internal.ReflectTools;
6565
import com.vaadin.flow.shared.Registration;
6666
import com.vaadin.flow.signals.Signal;
67-
import com.vaadin.flow.signals.impl.Effect;
6867
import com.vaadin.flow.signals.impl.UsageTracker;
6968
import com.vaadin.flow.signals.local.ValueSignal;
7069

@@ -382,7 +381,7 @@ void setIsAppliedPredicate(
382381
* {@link BindingBuilder#withValidator(Validator)}.
383382
* <p>
384383
* The Binder automatically runs validators inside a
385-
* {@link com.vaadin.flow.signals.impl.Effect#effect(Component, com.vaadin.flow.function.SerializableRunnable)}
384+
* {@link Signal#effect(Component, com.vaadin.flow.function.SerializableRunnable)}
386385
* context. This makes validators reactive to signal changes - when you
387386
* call {@code value()} on another binding from within a validator, the
388387
* validator will automatically re-run whenever that other binding's
@@ -435,7 +434,7 @@ void setIsAppliedPredicate(
435434
*
436435
* @see BindingBuilder#withValidator(Validator)
437436
* @see com.vaadin.flow.component.HasValue#bindValue
438-
* @see com.vaadin.flow.signals.impl.Effect#effect(Component,
437+
* @see Signal#effect(Component,
439438
* com.vaadin.flow.function.SerializableRunnable)
440439
*
441440
* @since 25.1
@@ -1711,7 +1710,7 @@ private FIELDVALUE convertToFieldType(TARGET target) {
17111710
private void initInternalSignalEffectForValidators() {
17121711
if (signalRegistration == null
17131712
&& getField() instanceof Component component) {
1714-
signalRegistration = Effect.effect(component, () -> {
1713+
signalRegistration = Signal.effect(component, () -> {
17151714
if (valueInit) {
17161715
// start to track signal usage
17171716
doConversion();

flow-data/src/test/java/com/vaadin/flow/data/binder/BinderSignalTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import com.vaadin.flow.dom.SignalsUnitTest;
2929
import com.vaadin.flow.function.SerializablePredicate;
3030
import com.vaadin.flow.signals.Signal;
31-
import com.vaadin.flow.signals.impl.Effect;
3231
import com.vaadin.flow.signals.local.ValueSignal;
3332
import com.vaadin.flow.tests.data.bean.Person;
3433

@@ -864,7 +863,7 @@ private void testStatusChangeRunEffects(Runnable binderSetup) {
864863

865864
AtomicInteger effectCalled = new AtomicInteger(0);
866865
AtomicBoolean prevStatus = new AtomicBoolean(true);
867-
Effect.effect(firstNameField, () -> {
866+
Signal.effect(firstNameField, () -> {
868867
prevStatus.set(binder.getValidationStatus().get().isOk());
869868
effectCalled.incrementAndGet();
870869
});
@@ -910,7 +909,7 @@ private void testInitialStatusChangeRunEffects(
910909
binderSetup.accept(item);
911910

912911
AtomicBoolean prevStatus = new AtomicBoolean(true);
913-
Effect.effect(firstNameField, () -> {
912+
Signal.effect(firstNameField, () -> {
914913
prevStatus.set(binder.getValidationStatus().get().isOk());
915914
});
916915

flow-server/src/main/java/com/vaadin/flow/component/SignalPropertySupport.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import com.vaadin.flow.shared.Registration;
2323
import com.vaadin.flow.signals.BindingActiveException;
2424
import com.vaadin.flow.signals.Signal;
25-
import com.vaadin.flow.signals.impl.Effect;
2625

2726
/**
2827
* Helper class for binding a {@link Signal} to a property of a
@@ -131,7 +130,7 @@ public void bind(Signal<T> signal) {
131130
throw new BindingActiveException();
132131
}
133132
this.signal = signal;
134-
registration = Effect.effect(owner, () -> {
133+
registration = Signal.effect(owner, () -> {
135134
value = signal.get();
136135
valueChangeConsumer.accept(value);
137136
});

flow-server/src/main/java/com/vaadin/flow/dom/ElementEffect.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@
4242
* context of a given element's life-cycle.
4343
* <p>
4444
* It ultimately creates a Signal effect, i.e. a call to
45-
* {@link Signal#effect(EffectAction)}, that is automatically enabled when an
46-
* element is attached and disabled when the element is detached. Additionally,
47-
* it provides methods to bind signals to element according to a given value
48-
* setting function.
45+
* {@link Signal#unboundEffect(EffectAction)}, that is automatically enabled
46+
* when an element is attached and disabled when the element is detached.
47+
* Additionally, it provides methods to bind signals to element according to a
48+
* given value setting function.
4949
* <p>
5050
* For internal use only. May be renamed or removed in a future release.
5151
*
@@ -98,7 +98,7 @@ public ElementEffect(Element owner, SerializableRunnable effectFunction) {
9898
* effect.remove(); // to remove the effect when no longer needed
9999
* </pre>
100100
*
101-
* @see Signal#effect(EffectAction)
101+
* @see Signal#unboundEffect(EffectAction)
102102
* @param owner
103103
* the owner element for which the effect is applied, must not be
104104
* <code>null</code>
@@ -131,7 +131,7 @@ public static Registration effect(Element owner,
131131
* Element::setVisible);
132132
* </pre>
133133
*
134-
* @see Signal#effect(EffectAction)
134+
* @see Signal#unboundEffect(EffectAction)
135135
* @param owner
136136
* the owner element for which the effect is applied, must not be
137137
* <code>null</code>

flow-server/src/main/java/com/vaadin/flow/signals/Signal.java

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020

2121
import org.jspecify.annotations.Nullable;
2222

23+
import com.vaadin.flow.component.Component;
24+
import com.vaadin.flow.dom.ElementEffect;
25+
import com.vaadin.flow.function.SerializableRunnable;
26+
import com.vaadin.flow.shared.Registration;
2327
import com.vaadin.flow.signals.function.CleanupCallback;
2428
import com.vaadin.flow.signals.function.EffectAction;
2529
import com.vaadin.flow.signals.function.SignalComputation;
@@ -37,10 +41,10 @@
3741
* A signal is a reactive value holder with automatic subscription and
3842
* unsubscription of listeners.
3943
* <p>
40-
* Reactivity is based on {@link Signal#effect(EffectAction)} callbacks that
41-
* detect the signals used during invocation. The callback will be run again
42-
* whenever there's a change to any of the signal instances used in the previous
43-
* invocation. Detection is based on running {@link #get()}.
44+
* Reactivity is based on {@link Signal#unboundEffect(EffectAction)} callbacks
45+
* that detect the signals used during invocation. The callback will be run
46+
* again whenever there's a change to any of the signal instances used in the
47+
* previous invocation. Detection is based on running {@link #get()}.
4448
* {@link #untracked(ValueSupplier)} can be used to read the value within an
4549
* effect without registering a dependency.
4650
* <p>
@@ -64,7 +68,7 @@ public interface Signal<T> extends Serializable {
6468
* transaction depend on the value so that the transaction fails in case the
6569
* signal value is changed concurrently.
6670
* <p>
67-
* Reading the value inside an {@link #effect(EffectAction)} or
71+
* Reading the value inside an {@link #unboundEffect(EffectAction)} or
6872
* {@link #computed(SignalComputation)} callback sets up that effect or
6973
* computed signal to depend on the signal.
7074
*
@@ -117,16 +121,50 @@ default <C> Signal<C> map(SignalMapper<T, C> mapper) {
117121
*/
118122

119123
/**
120-
* Creates a signal effect with the given action. The action is run when the
121-
* effect is created and is subsequently run again whenever there's a change
122-
* to any signal value that was read during the last invocation.
124+
* Creates a signal effect that is owned by a given component. The effect is
125+
* enabled when the component is attached and automatically disabled when it
126+
* is detached.
127+
* <p>
128+
* Example of usage:
129+
*
130+
* <pre>
131+
* Registration effect = Signal.effect(myComponent, () -> {
132+
* Notification.show("Component is attached and signal value is "
133+
* + someSignal.get());
134+
* });
135+
* effect.remove(); // to remove the effect when no longer needed
136+
* </pre>
137+
*
138+
* @param <C>
139+
* the type of the component
140+
* @param owner
141+
* the owner component for which the effect is applied, must not
142+
* be <code>null</code>
143+
* @param effectFunction
144+
* the effect function to be executed when any dependency is
145+
* changed, must not be <code>null</code>
146+
* @return a {@link Registration} that can be used to remove the effect
147+
* function
148+
*/
149+
static <C extends Component> Registration effect(C owner,
150+
SerializableRunnable effectFunction) {
151+
return ElementEffect.effect(owner.getElement(), effectFunction);
152+
}
153+
154+
/**
155+
* Creates an unbound signal effect with the given action. The action is run
156+
* when the effect is created and is subsequently run again whenever there's
157+
* a change to any signal value that was read during the last invocation.
158+
* <p>
159+
* Consider using {@link #effect(Component, SerializableRunnable)} instead
160+
* to tie the effect lifecycle to a component.
123161
*
124162
* @param action
125163
* the effect action to use, not <code>null</code>
126164
* @return a callback used to close the effect so that it no longer listens
127165
* to signal changes, not <code>null</code>
128166
*/
129-
static CleanupCallback effect(EffectAction action) {
167+
static CleanupCallback unboundEffect(EffectAction action) {
130168
Effect effect = new Effect(Objects.requireNonNull(action));
131169
return effect::dispose;
132170
}
@@ -140,10 +178,10 @@ static CleanupCallback effect(EffectAction action) {
140178
* and only if the previously computed value might have been invalidated by
141179
* dependent signal changes. If the computation callback throws a
142180
* {@link RuntimeException}, then that exception will be re-thrown when
143-
* accessing the signal value. An {@link Signal#effect(EffectAction) effect}
144-
* or computed signal that uses the value from a computed signal will not be
145-
* invalidated if the computation is run again but produces the same value
146-
* as before.
181+
* accessing the signal value. An {@link Signal#unboundEffect(EffectAction)
182+
* effect} or computed signal that uses the value from a computed signal
183+
* will not be invalidated if the computation is run again but produces the
184+
* same value as before.
147185
*
148186
* @param <T>
149187
* the signal type

flow-server/src/main/java/com/vaadin/flow/signals/function/CleanupCallback.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
* dispose of resources, or cancel an ongoing operation.
2525
* <p>
2626
* This is typically returned from registration methods such as
27-
* {@link Signal#effect(EffectAction)} to allow the caller to clean up the
28-
* registration when it's no longer needed.
27+
* {@link Signal#unboundEffect(EffectAction)} to allow the caller to clean up
28+
* the registration when it's no longer needed.
2929
*
30-
* @see Signal#effect(EffectAction)
30+
* @see Signal#unboundEffect(EffectAction)
3131
*/
3232
@FunctionalInterface
3333
public interface CleanupCallback extends Serializable {

flow-server/src/main/java/com/vaadin/flow/signals/function/EffectAction.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
* during the action execution. When any of those signals change, the action is
2929
* re-run with updated dependencies.
3030
*
31-
* @see Signal#effect(EffectAction)
31+
* @see Signal#unboundEffect(EffectAction)
3232
*/
3333
@FunctionalInterface
3434
public interface EffectAction extends Serializable {

flow-server/src/main/java/com/vaadin/flow/signals/impl/ComputedSignal.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@
4141
* changed for any of the signals that were used when computing the previous
4242
* value. If the computation callback throws a {@link RuntimeException}, then
4343
* that exception will be re-thrown when accessing the value of this signal. An
44-
* {@link Signal#effect(EffectAction) effect} or computed signal that uses the
45-
* value from a computed signal will not be invalidated if the computation is
46-
* run again but produces the same value as before.
44+
* {@link Signal#unboundEffect(EffectAction) effect} or computed signal that
45+
* uses the value from a computed signal will not be invalidated if the
46+
* computation is run again but produces the same value as before.
4747
*
4848
* @param <T>
4949
* the value type

flow-server/src/main/java/com/vaadin/flow/signals/impl/Effect.java

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@
2323

2424
import org.jspecify.annotations.Nullable;
2525

26-
import com.vaadin.flow.component.Component;
27-
import com.vaadin.flow.dom.ElementEffect;
2826
import com.vaadin.flow.function.SerializableRunnable;
29-
import com.vaadin.flow.shared.Registration;
3027
import com.vaadin.flow.signals.SignalEnvironment;
3128
import com.vaadin.flow.signals.function.CleanupCallback;
3229
import com.vaadin.flow.signals.function.EffectAction;
@@ -191,35 +188,4 @@ public synchronized void dispose() {
191188
action = null;
192189
}
193190

194-
/**
195-
* Creates a Signal effect that is owned by a given component. The effect is
196-
* enabled when the component is attached and automatically disabled when it
197-
* is detached.
198-
* <p>
199-
* Example of usage:
200-
*
201-
* <pre>
202-
* Registration effect = Effect.effect(myComponent, () -> {
203-
* Notification.show("Component is attached and signal value is "
204-
* + someSignal.get());
205-
* });
206-
* effect.remove(); // to remove the effect when no longer needed
207-
* </pre>
208-
*
209-
* @param <C>
210-
* the type of the component
211-
* @param owner
212-
* the owner component for which the effect is applied, must not
213-
* be <code>null</code>
214-
* @param effectFunction
215-
* the effect function to be executed when any dependency is
216-
* changed, must not be <code>null</code>
217-
* @return a {@link Registration} that can be used to remove the effect
218-
* function
219-
*/
220-
public static <C extends Component> Registration effect(C owner,
221-
SerializableRunnable effectFunction) {
222-
return ElementEffect.effect(owner.getElement(), effectFunction);
223-
}
224-
225191
}

flow-server/src/test/java/com/vaadin/flow/component/AbstractFieldBindValueTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import com.vaadin.flow.internal.nodefeature.SignalBindingFeature;
2626
import com.vaadin.flow.signals.BindingActiveException;
2727
import com.vaadin.flow.signals.Signal;
28-
import com.vaadin.flow.signals.impl.Effect;
2928
import com.vaadin.flow.signals.local.ValueSignal;
3029

3130
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -232,7 +231,7 @@ public void bindValue_setValue_countEffectExecutions() {
232231
input.bindValue(signal, signal::set);
233232

234233
AtomicInteger counter = new AtomicInteger(0);
235-
Effect.effect(input, () -> {
234+
Signal.effect(input, () -> {
236235
signal.get();
237236
counter.incrementAndGet();
238237
});

0 commit comments

Comments
 (0)