koliseoapi / alt-ng

A flux implementation without the boilerplate
http://alt-ng.koliseo.com/
MIT License
16 stars 2 forks source link

What new features exist in alt-ng? #4

Open CrazyPython opened 6 years ago

CrazyPython commented 6 years ago

It isn't immediately clear from the documentation what new stuff has been added to alt-ng. http://alt-ng.koliseo.com/doc/migrating-from-alt only says the breaking changes, it does not tell you what new things you could do with alt-ng.

Can we have a Changelog that diffs against goatslacker/alt?

icoloma commented 6 years ago

The differences should be more or less apparent after following the tutorial. The most relevant ones:

That's mostly it IIRC. You may also be interested in comparing with unstated (and please let us know your thoughts).

We should totally add a Changelog in general, though.

CrazyPython commented 6 years ago

Promises returned by Actions will be resolved before forwarding the resulting value.

Interesting. I've been using a model where Actions are only for delivering the results of data- "services" are called from the store, and I use Alt's DataSources API to bind the service to specific actions. I like how declarative the API is.

Action.preventDefault() can be used to prevent default dispatching from your action (to do your own thing instead).

Can you give me an example of when this would be useful?

alt.createActions() is simpler to use (well, "simpler" is opinionated, but we are quite confident about this).

Are there TypeScript definitions for it? I posted a declarative way to generate actions that's more verbose, but easier for type checkers to autocomplete.

AltContainer can merge properties from multiple Stores

I think multiple stores sharing the same namespace for internal properties can get confusing- you don't want to worry about another store's naming when writing one store.

By the way, have you seen https://gitter.im/goatslacker/alt? I've been posting various techniques there.

You may also be interested in comparing with unstated unstated seems to be similar to the state management system I had before: most state stays locally. I used a HOC to connect my ViewControllers to re-render every time the store changed. It got confusing as I needed more and more global state.

After that one got too messy, I went in search of something that was 1) simpler than redux 2) capable of handling complex applications, and I found alt. I've removed a few warnings and botched some TypeScript definitions in my fork (crazypython/alt), but that's about it.

CrazyPython commented 6 years ago

By the way, does this use code from the original Alt?

CrazyPython commented 6 years ago

How do you structure your stores? I do it like this. index.js contains the stores themselves, and m.js instantiates the store. Actions and stores register themselves with the Alt in m.js after they're imported. screen shot 2018-07-04 at 1 02 09 pm

The top level index.js imports all the Stores in the correct order (my stores form a DAG), and then creates an object with all the actions merged together. Actions are separate from stores because stores sometimes cross-depend on each other for actions, and sometimes one UI interaction needs multiple stores to update.

I watched Jing Chen's original two talks on Flux, and I found them helpful.


In Koliseo, do views trigger asynchronous actions?

The app I'm building (with React Native) has to interact with two data sources. The first is my own local database. The second is Apple PhotoKit. Some albums and some assets are linked between PhotoKit and my app. Apple PhotoKit has UUIDs for entities inside of it, but those UUIDs only persist across one physical device. So, phone loss = UUIDs wiped. They do have cloud identifiers, but they're marked as private and undocumented. I store the UUIDs and the creationDate in my own internal database. If the UUID isn't found in PhotoKit, I look up entities using the looking at the millisecond-accurate creationDate of each.

However, albums don't have creationDates. Instead, when I find that an album with that UUID doesn't exist, I look for the album's assets by using the asset's creationDates, making a guess as to the UUID of the album based on the albums the album's assets are in.

These lookup operations can get expensive, so I cache the UUIDs on my end.

I use a FlatList, which is the React Native lazy-loading list. I only want to retrieve albums when they're needed, and I want the ViewController to not have to care if 1) a simple UUID lookup is being performed 2) an expensive creationDate lookup is being performed 3) the image is being downloaded from the server.

My current model of a solution is to have a lazy Albums class and a lazy Asset class. The lazy Albums class has a getter returns an array of lazy assets. It then fires an asynchronous request to start looking for the album– if the album's contents or name changes, we'll want to show it in our app. I haven't investigated using a PhotoKit listener, but I suspect my app will only receive updates when it's in the foreground.

The lazy assets begin their own process of finding the asset (if it hasn't been found yet) when they're asked to render. Asset#Thumbnail returns the given loading spinner or an <Image/> if the image has been located and is downloading.

Do you think there's a better approach than my current method? I would like to have a simple and flexible mental model, because I would like to integrate with Google Photos later on (which I can only access when I have internet), and some users may like having using Google Photos and Apple Photos simultaneously, so that they can see their photos in the "Photos" app.

Thanks for reading this, I would like to hear what you have to say.

icoloma commented 6 years ago

The only use case for Action.preventDefault() that I can remember is paginated listings, where you trigger one action but there are multiple events associated to that. Maybe you want only "loading" and "results retrieved" or "error response", or maybe you want to add each page of results as they are being retrieved. It's for cases where you want precise control of the event loop.

TypeScript is something that we are looking forward to, but right now it's not a good time (other priorities in the way).

I am aware of the alt2 branch, but it came out a bit too late. This implementation is inspired by Alt, but does not reuse code from alt.

About multiple properties overlapping between stores, truth is that it has never happened to us (yet). It would be trivial to throw an error or add a callback to merge properties, but we found the default behavior of alt to always separate stores too verbose for the most common case.

We do not merge different actions in a single object, and rather import them where needed (actions and stores are usually put together in a different package separated by the vertical, e.g. users or invoices). Stores are also independent to each other in our setup, and with a complex setup like you describe I would also consider Redux (the immutability and reduction paradigm seems like a better fit if state is dependent to each other). You can do the same thing with either alt-ng or alt, but redux forces you this way.

Your lazy implementation sounds good. If it was more native I would consider using a low level library for assets like Picasso, there are similar tools for React Native (like react-native-fast-image) but I don't have first-hand experience with them.

Hope this helps!

CrazyPython commented 6 years ago

Thank you for the insight!

The only use case for Action.preventDefault() that I can remember is paginated listings, where you trigger one action but there are multiple events associated to that.

Could you go into more detail here? I might also need to paginate, I may need to lazy load my metadata for performance and iCloud limitations.

Thanks for the Redux suggestion. Right now I only have one cross-store data dependency, where objects from one store are transferred to another store. If it grows unwieldy (due to Google Photos, shared albums and whatnot), I'll definitely look into Redux. (I also have a NavigationStore for firing navigation actions and overriding them for onboarding; it independently listens to the same actions as other stores)

I looked at Unstated a bit more closely today. I like the idea of keeping state local, and I will look into React Final Form (linked by Unstated). I'm going to investigate both more when I implement the interface for adding metadata to albums, one for the core aspects of the app.

I like the native suggestion. I will consider implementing my own ImageLoader and do all the image resolution on that side, where the Stores manage storing and providing cached metadata, but the native side is responsible for using and sending back the cached metadata. The libraries you linked were for fetching remote images, and for me, the most complex part is retrieving the metadata necessary to fetch the remote image.

Again, thanks for the insight! It's really helpful.