I'm trying to update input to a textfield using a Just publisher and a local state. Whenever the the view re-renders as result of a change to the local state the onRecieve modifier gets triggered. Here's a code sample to showcase the underlying issue.
struct MyTextField: View {
@State private var someLocalState = ""
@State private var inputText = ""
var body: some View {
TextField("Enter Text", text: $inputText)
.onReceive(Just(inputText)) { newValue in
print(newValue)
// do something, store result in localstate
someLocalState = doSomething() // this causes onRecieve to trigger and print again
if someLocalState meets some condition {
inputText = someLocalState
}
}
}
}
I always get an extra print. How do I prevent onRecieve from triggering when someLocalState updates
You shouldn't be using onReceive
with Just
to observe a @State
value.
Instead, use onChange(of:)
, which was designed for this purpose exactly - executing side effects when a non-published state changes.
struct MyTextField: View {
@State private var someLocalState = ""
@State private var inputText = ""
var body: some View {
TextField("Enter Text", text: $inputText)
.onChange(of: inputText) { newValue in
print(newValue)
// do something, store result in localstate
someLocalState = doSomething() // this causes onRecieve to trigger and print again
}
}
}
onChange(of:)
is only available on iOS 14, so if you are targeting iOS 13, you can't use it unfortunately.
If you need a solution for iOS 13, you should check whether the value has actually changed and only execute the state update in case the value is different than the old value.
.onChange(of: inputText) { newValue in
print(newValue)
guard newValue != inputText else { return }
// do something, store result in localstate
someLocalState = doSomething() // this causes onRecieve to trigger and print again
}