cmeeren / yarni

Yarni – Yet another Redux .NET implementation
MIT License
6 stars 1 forks source link

Possible usage example #2

Open jjwilliams42 opened 6 years ago

jjwilliams42 commented 6 years ago

I realize that you said "This is not intended for public consumption, and I will likely not maintain this", but I've looked at Redux.NET (and the umerged PR's you did), and I like the way your comment in https://github.com/GuillaumeSalles/redux.NET/issues/51#issuecomment-289406335 composes state and reducers in Yarni. Your library also doesn't depend on IObservable (that I've seen).

I would love to see some examples of Yarni:

General Usage Reducer Composition State Composition How you handle async things (calling apis) Subscribing(?) to parts of the state

We have a WPF application that is growing unwieldy, and have been looking at alternative state management techniques.

I understand that you have no intentions of maintaining anything, but some examples would be awesome.

cmeeren commented 6 years ago

I don't use Yarni anymore and haven't for a while, I've moved to F# and implemented my own store there. Still, I can give a few examples. (Sorry if they don't compile - I've only used F# for a while now.)

General Usage Reducer Composition State Composition

Unsure what you mean. IIRC this works exactly the same as in redux.NET.

How you handle async things (calling apis)

I do this using the listener middleware included in Yarni. I did the same thing in redux.NET, but the middleware wasn't included there. I think this is how it's used:

// A listener is essentially a function that takes an action, a state (before the action
// is sent to the reducers), and a dispatch function. For example:
class ApiListener {
  void CallApi(object action, RootState beforeState, Action<object> dispatch) {
    if (!(action is SignInRequested a)) return;
    var response = CallSignInApiWith(a.Username, a.Password);
    if (response.IsSuccessful) dispatch(new SignInSuccessful(response.AccessToken));
    else dispatch(new SignInFailed(response.Error));
  }
}

// Create the middleware
var listenerMiddleware = ListenerMiddleware.CreateMiddleware();

// Subscribe listeners
listenerMiddleware.ActionReceived += myApiListenerInstance.CallApi;

// Create store with middleware
var store = new Store(..., new[]{listenerMiddleware})

Currently (in F#, though that's irrelevant), I just use proper middleware instead of listeners, because it gives me more flexibility regarding when to pass the action down the chain, if/how to use the state before/after updating it, etc. Also it allows me to skip the concept of a "listener" and just use the existing middleware concept. Though it comes with a slightly larger chance of bugs, so YMMV.

Subscribing(?) to parts of the state

I actually made something I called ProjectingStore for this (see https://github.com/GuillaumeSalles/redux.NET/issues/51#issuecomment-290390321 and the linked blog post). In short, it was a store implementation (or wrapper) that did not make the "raw" state available though State or StateChanged. Instead, the interface was basically this:

interface IProjectingStore<TState> {
  /// Publishes changes to the given projection of the store.
  /// Observable.DistinctUntilChanged is used so that only actual changes
  /// to the projection are published.
  IObservable<TProjection> Observe<TProjection>(Func<TState, TProjection> projection);

  /// Returns the given projection of the current state.
  TProjection Project<TProjection>(Func<TState, TProjection> projection);
}