Description
Checklist
- This is not a bug caused by platform.
- Reviewed the README and documentation.
- Checked existing issues & PRs to ensure not duplicated.
What happened?
Consider a simple SwiftUI app:
@main
struct _App: App {
var body: some Scene {
WindowGroup {
__App__
}
}
}
where __App__
is a placeholder for either plain SwiftUI App or its Hooks alternative.
SwiftUI App
struct SwiftUIApp: View {
var body: some View {
let _ = print("SwiftUI - I changed")
Text("")
}
}
Hooks App
func HooksApp() -> some View {
HookScope {
let _ = print("HookScope - I changed")
Text("")
}
}
When you run both apps from Xcode 13.4.1 on iOS 15.5, you will see the following output in the console:
SwiftUIApp() |
HooksApp() |
---|---|
SwiftUI - I changed |
HookScope - I changed HookScope - I changed HookScope - I changed |
Considering both apps are stateless and do not invalidate its body, the console result should match.
When tracking down the issue using SwiftUI's _printChange()
, it's clear that it's caused by library's HookScopeBody
struct:
private struct HookScopeBody<Content: View>: View {
@Environment(\.self) private var environment
...
var body: some View {
if #available(iOS 15.0, *) {
+ let _ = Self._printChanges()
dispatcher.scoped(environment: environment, content)
}
}
Changes printed by SwiftUI are:
HookScopeBody: @self, @identity, _dispatcher, _environment changed.
HookScopeBody: _environment changed.
HookScopeBody: _environment changed.
The problem is in observing Environment(\.self)
which invalidates HookScopeBody
's body for each change in environment, causing a view to re-render.
I haven't dig into finding a solution yet, but wanted to keep the issue on sight as it can potentially cause unexpected re-renders of the whole app.
Notes:
⚠️ HookView
is also affected (as it internally usesHookScope
)⚠️ When working on a solution, we need to keep in mind thatContext
internally uses environments too.
Expected Behavior
HookScope
body should only invalidate for key path specified in useEnvironment
or types used in useContext
.
Reproduction Steps
- Create a new project and replace @main struct with
_App
struct from above - Replace the
__App__
withSwiftUIApp()
and run - Replace
SwiftUIApp()
call withHooksApp()
- Compare console outputs
Swift Version
5.6+
Library Version
<= 0.0.8
Platform
No response
Scrrenshot/Video/Gif
No response