Action Sheet Data on Handler Event

Has any thought been given to having handler in action sheet passing selected value from handler event to onDidDismiss as a data property?

Trying to avoid mutable state when working with multiple chained action sheets, and current status quo isn’t great.

Perhaps I’m missing something, I know you can manually close an action sheet with e.g. x.dismiss(someData), but I’m looking for a way to return data from handler event.

Would it be fair of me to rephrase your quest thusly?

I want to easily determine which button triggered the action sheet dismissal from the subscriber to onDidDismiss

In other words, are you absolutely fixated on hijacking data for this, or will role suffice? You can declare custom roles for every button in your action sheet, and if their handlers return something truthy (indicating that the action sheet should be dismissed), the role of that button is included in the dismissal object.

Thanks, interesting hack, does the trick (for string only), but I think it would be better for the framework to expose the data button property along with role, text, etc. rather than hijacking role for this purpose.

I don’t see why x.dismiss(someData) populates the data property while passing it in button props does nothing.

We’re going to have to agree to disagree about “better”, but I want to push back a bit on your use of “hijack” here. I deliberately used that term in my previous post, because data has a clearly defined and publicly documented behavior now, as something that is totally independent of even the notion of “buttons”: whoever calls dismiss gets to populate data. I don’t see how one could alter this to match your desire without breaking the idea that "data is what got passed to the dismiss call".

role, OTOH, predates even the ability to return data from an action sheet dismissal call. role used to be all that you got back when action sheets were dismissed. So I don’t think it’s fair to call my suggestion “hijacking” role, as I think the way I’m suggesting using role is completely in line with the way it’s always been intended.

Check out dismiss in the v3 source, which takes (and forwards) role, while explicitly sending null in the data parameter to the equivalent generic overlay handler of its time: ViewController.

Well, role only works for string data types, so clearly it’s not a viable substitute for the use cases that the data property was intended for.

Obviously it’s an edge case, most users aren’t chaining ActionSheet instances building up a data object on-the-fly. For a single ActionSheet instance maintaining state in button handler isn’t a big deal, but when you have a chain of instances to deal with, mutable state approach is less than ideal.

I’m thinking along these lines:

this.action.create({ buttons: [{data: ...}], ... }).then(outer => {
  outer.onDidDismiss().then(x => {
    this.action.create({...}).then(inner => {
      const d = x.data // data threaded through, no instance level props
      inner.onDidDismiss().then(...)
      inner.present()
    })
  })
  outer.present()
})

Personally, I would invert this design and get data management out of the action sheets, because it smells weird to me to have every single action sheet need to know the shape of the overall elephant being built. When one additional link is put in the chain, now that change touches every part of the wizard. In order to get around that, it’s going to be tempting to type data as any, which nerfs typing and precipitates much crying amongst kittens galaxywide.

If the “elephant building” is instead hoisted out into a service, each action sheet need only be responsible for returning its bit, and I would find that a much more manageable state of affairs.

it’s going to be tempting to type data as any , which nerfs typing and precipitates much crying amongst kittens galaxywide.

The object being built has a well defined structure, and is built up based on various conditions (i.e. action sheet choices), so data: any is not a concern; however, mutable state is, and that’s what you’re forced into due to current ActionSheet design depending entirely on side effecting handler: () => void button prop.

If data prop were exposed for each buttons element, then one could thread through object state without having to maintain a class level mutable instance, and all the brittleness that that entails.

Perhaps breaking state management for this piece into a service might make sense, but it’s mutable state regardless; handler: () => void is the culprit.

Then I guess I’m not understanding the situation clearly, because I can’t see how I could write this while ensuring that both of the following conditions hold:

  • adding a new field to the end of the object being built can be done without any effect on types visible to every other link in the action sheet chain;
  • strongly-typed data

It’s true that the proposed button data type will be any (there’s no other viable base type the framework authors could choose), but the user simply casts it to the known/actual type at use site.

There is no mutation per se, since changes are threaded through the callback chain, vs. status quo instance level approach where mutation can occur at any point, which leaves the door wide open for suprising/buggy behavior.

With button data prop it would look something like the contrived non-pretty example below:

type Props = { a?: A, b?: B, c?: C, ... }

export class SomeComponent implements OnInit {

someAction() {
  this.action
    .create({ buttons: [{data: ...}], ... })
    .then(outer => {
      outer.onDidDismiss().then(x => {
        this.action
          .create({ buttons: [{data: ...}], ... })
          .then(inner => {
            inner.onDidDismiss().then(...)
            inner.present()
          })
        })
      outer.present()
    })
  }
}

In each onDidDismiss() promise the progressively built up data property is available. While instance level approach works, I’d prefer threading through state rather than maintaining it at class level.