angular-redux / store

Angular 2+ bindings for Redux
MIT License
1.34k stars 202 forks source link

Possible to write Actions as functions instead of Injectable Classes? #468

Closed ghost closed 6 years ago

ghost commented 7 years ago

This is a...

What toolchain are you using for transpilation/bundling?

Environment

NodeJS Version: 6.11 Typescript Version: 2.4.2 Angular Version: 4.2 @angular-redux/store version: 6.5.7 @angular/cli version: (if applicable) OS: macOS High Sierra 10.13 (17A365)

Link to repo showing the issus

(optional, but helps a lot)

Expected Behaviour:

To be able to write Actions like this: https://github.com/srtucker22/chatty/blob/1c8d7a0265167201619c4dff1c3c5695f934c209/client/src/actions/auth.actions.js

Actual Behaviour:

Forced to use Angular syntax.

Additional Notes:

This may be just the nature of Angular, and I suspect it is, but I was curious about the ability to write in a more functional style. Is it possible without using dependency injection, classes, or component decorators?

gregkopp commented 7 years ago

I'm not exactly sure what you are asking, but if this is what you were pointing to:

export const logout = () => {
  client.resetStore();
  wsClient.unsubscribeAll(); // unsubscribe from all subscriptions
  wsClient.close(); // close the WebSocket connection
  return { type: LOGOUT };
};

Then I see problems here. Your action is performing extra functions that kind of violate some aspects of redux. The wsClient calls should be handled in an Epic. Or am I missing something?

For example, I am thinking you would want an action for "logoutRequested," which gets handled in an Epic (not the reducer) to perform your extra calls. The epic would then return another action "logoutSucceeded," which would then get handled in the reducer to update the state. If for some reason the extra wsClient calls fail, you could dispatch an error action that would be handled differently.

ghost commented 7 years ago

Sorry, I could have made that more clear, and provided a less confusing example. Epics aside, I'm asking if I can write actions as functions instead of classes/decorators.

gregkopp commented 7 years ago

I don't know if this helps, but this is how I write my actions:

export abstract class WidgetsActions {
  static readonly onWidgetsDataRequest = () => ({ type: WidgetsTypes.WIDGETS_DATA_REQUESTED });
  static readonly onWidgetsDataReceipt = (data: Widget[]) => ({ type: WidgetsTypes.WIDGETS_DATA_RECEIVED, payload: data });
}

But I am not as versed in FP as I am in OOP, so I'm not sure if I can be of more help.

smithad15 commented 7 years ago

Yes definitely. If you would like to use the classic Action Creator pattern as seen here (http://redux.js.org/docs/basics/Actions.html#action-creators) that is supported.

ghost commented 7 years ago

Nice @smithad15. Thanks. I guess what I'm worried about is the Angular-specific DI (@Injectable) and class constructors (constructor(private ngRedux: NgRedux<IAppState>) {}). Will using the more functional pattern affect unit testing with MockNgRedux, etc?

smithad15 commented 7 years ago

It will change your testing approach a bit, yes. If you were creating your own mock object before that was filled with stubs i.e.:

export class MockActions { 
  doThings = jasmine.createSpy(‘doThings’);
}

You would no longer have to do that since you would be importing the function directly instead of through Angular’s DI system. What I will often do is first make sure I have a simple unit test for my action creator function and then in my component I won’t worry about stubbing the function out and instead just make sure that the appropriate object is sent to dispatch. One less step to worry about and you also get double coverage of the action creator function (and therefore more integration style coverage from your unit test suite). Same approach can be made with selectors.

There are quite a few different patterns you can do with this library. It all depends on your preferred style and testing story (some are simpler for initial implementation, some are simpler for unit testing). Hope that helps.

ghost commented 6 years ago

Thanks @smithad15.

ghost commented 6 years ago

Oh, one last question. Given your last paragraph there, would you be losing the benefits of https://github.com/angular-redux/store/blob/master/articles/epics.md or https://github.com/angular-redux/store/blob/master/articles/action-creator-service.md by writing in a purely functional style vs Angular DI/Classes?

smithad15 commented 6 years ago

Epics would be fine, it's the same concerns as mentioned before regarding testing and mocking. Purely functional would replace action creator services entirely.