Skip to content

[Bug]: Atom effect receives context with unintended scope #178

@ra1028

Description

@ra1028

Checklist

  • This is not a bug caused by platform.
  • Reviewed the README and documentation.
  • Checked existing issues & PRs to ensure not duplicated.

What happened?

The atom effect receives a context that refers to different scopes depending on whether the atom was initialized or updated.

In the example below, UpdateEffect inside AtomB gets the state of AtomA from the scope where AtomB was initialized.
On the other hand, CustomEffect gets the state of AtomA from the scope where the update happened.

So, if the initialization and update of AtomB happen in different scopes, then it'll end up getting separate states of AtomA.

struct AtomA: StateAtom, Scoped, Hashable {
  func defaultValue(context: Context) -> Bool {
    false
  }
}

struct AtomB: StateAtom, Hashable {
  func defaultValue(context: Context) -> Bool {
    false
  }

  func effect(context: CurrentContext) -> some AtomEffect {
    MergedEffect(
      CustomEffect()
      UpdateEffect {
        // Here gets AtomA's state for the scope where this AtomB is initialized.
        let a = context.read(AtomA())
        print("\(a)")
      }
    )
  }
}

final class CustomEffect: AtomEffect {
  func updated(context: Context) {
    // Here gets Atom's state for the scope where AtomB is updated.
    let a = context.read(AtomA())
    print("\(a)")
  }
}

Expected Behavior

To make the behavior more intuitive, I'd expect atom effects to always receive contexts that capture the scope where the atom was initialized.

Reproduction Steps

In the example described in What happened? section;

  1. Watch AtomA in a view.
  2. Watch AtomB in a scoped view.
  3. Update AtomB in the scoped view.
struct ViewA: View {
    @Watch(AtomA())
    var stateA

    var body: some View {
        AtomScope {
            VStack {
                Text(stateA ? "A: true" : "A: false")
                ViewB()
            }
        }
    }
}

struct ViewB: View {
    @WatchState(AtomA())
    var stateA

    @WatchState(AtomB())
    var stateB

    var body: some View {
        VStack {
            Button("Update A") {
                stateA = true
            }
            Button("Update B") {
                stateB = true
            }
        }
    }
}

Swift Version

6.0

Library Version

0.7.6

Platform

iOS

Scrrenshot/Video/Gif

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions