How to Run an Action When a State Changes

How can I run an action when a state changes?

You can't use didSet observer on @State but you can on an ObservableObject property.

import SwiftUI
import Combine

final class SelectionStore: ObservableObject {
var selection: SectionType = .top {
didSet {
print("Selection changed to \(selection)")
}
}

// @Published var items = ["Jane Doe", "John Doe", "Bob"]
}

Then use it like this:

import SwiftUI

enum SectionType: String, CaseIterable {
case top = "Top"
case best = "Best"
}

struct ContentView : View {
@ObservedObject var store = SelectionStore()

var body: some View {
List {
Picker("Selection", selection: $store.selection) {
ForEach(FeedType.allCases, id: \.self) { type in
Text(type.rawValue).tag(type)
}
}.pickerStyle(SegmentedPickerStyle())

// ForEach(store.items) { item in
// Text(item)
// }
}
}
}

How to perform an action only when a state is changed?

The problem is that you're performing the check continuously, and for every check you execute the if-statement in which you send the IR code. Consequently, your application will keep sending IR codes as fast as it can.

If you only want to send do something (send an IR code) on a state change, you should remember the state, and perform an action on a state change instead.

This is very easy to implement by adding a variable previous_connection_state. Example code below (this would replace the loop() from your current code).

esp_a2d_connection_state_t previous_connection_state;

void loop() {
esp_a2d_connection_state_t connection_state = a2dp_sink.get_connection_state();

if (connection_state != previous_connection_state) {

if (connection_state == ESP_A2D_CONNECTION_STATE_CONNECTED) {
irsend.sendRaw(rawData, 71, 38);
}

if (connection_state == ESP_A2D_CONNECTION_STATE_DISCONNECTED) {
irsend.sendRaw(rawData2, 71, 38);
}

previous_connection_state = connection_state;
}
}

Execute Function when a State Variable Changes inside of a useEffect() Hook

Store graph in a React ref so it persists through rerenders. In hideN use an Optional Chaining operator on graphRef.current to call the findById function.

Add hideNode state as a dependency to the useEffect hook and move the hideN call out of the conditional block that is only instantiating a graph value to store in the ref.

const graphRef = useRef(null);
const ref = useRef(null);

//Hide Node State
const [hideNode, sethideNode] = useState("");

const hideN = () => {
const node = graphRef.current?.findById(hideNode);
node.hide();
};

useEffect(() => {
if (!graphRef.current) {
graphRef.current = new G6.Graph(cfg);
graphRef.current.data(data);
graphRef.current.render();
}
hideN();
}, [hideNode]);

UseEffect, Re-run on state change?

This kind of code execution cannot be implicit stopped.

Answer:
The useEffect-callback will be called again, even if the previous is not done

You could use a debounce or blocking behavior and cancel/ignore the previous action.
UseEffect supports a clean-up method. You could return a function to cancel a timer with a given throttle value (for debounce).

As stated by the react docs

Why did we return a function from our effect? This is the optional cleanup mechanism for effects. Every effect may return a function that cleans up after it. This lets us keep the logic for adding and removing subscriptions close to each other. They’re part of the same effect!

When exactly does React clean up an effect? React performs the cleanup when the component unmounts. However, as we learned earlier, effects run for every render and not just once. This is why React also cleans up effects from the previous render before running the effects next time. We’ll discuss why this helps avoid bugs and how to opt out of this behavior in case it creates performance issues later below.

Solution

Stackblitz Example

Is it possible to create a promise that get fulfilled by a state change in react-native?

Don't go polling. Instead, pass the resolve function as an argument to openInteractionModal, and call it together with setImportantState from the modal dialog.


If that is for some reason not possible, you can use a ref to store the resolve function and run it from an effect when the state changes:

const onImportantStateChange = useRef();
useEffect(() => {
if (!onImportantStateChange.current) return;
if (importantState === 'complete') {
onImportantStateChange.current('success');
onImportantStateChange.current = undefined;
} else if (importantState === 'failed') {
onImportantStateChange.current(Promise.reject(new Error('failure')));
onImportantStateChange.current = undefined;
}
}, [importantState])
const run = async () => {
return new Promise(resolve => {
onImportantStateChange.current = resolve;

// This function will open a modal
// where users will eventually change the important state
openInteractionModal();
});
}


Related Topics



Leave a reply



Submit