News for hackers. Stuff that matters.
Re:Opinionated Comparison of React, Angular2, and Aurelia by WA on Friday April 07, 2017 @06:44AM (#14058638)
Partially, you're right. Some logic happen in reducers, but not all, because they must be pure. For example, you can't call new Date() in a reducer, because it's not pure.

Imagine you have a TodoList with Items that have: a priority and a createdDate. TodoList is always sorted by 'priority, created DESC'. There's also an email service that gives you a high-level overview of the TodoList like so:

  High Prio (3 items)
  Mid Prio (1 item)
  Low Prio (0 items)
  Total: 4 items, last created: 2017-04-07 11:38
This email is sent out whenever a new item is added.

So, your business logic is like this:

If a user creates a new TodoItem:

- You need to get the current date (unpure, so this must come from an action creator [1])

- You must update the "entity" of TodoList and sort and recalculate its summary

- This summary must be send out as an email

Or to sum it up: You have an action that triggers a bunch of totally unrelated side effects. This has nothing to do with the state of the View, which Redux manages. But it's still important business logic to this app.

The question now is: How do you manage that? Because this clearly must be outside of React and Redux. Some people just slap other libraries on top, but I never found a satisfactory explanation on how to implement such workflows "properly".

Too many tutorials are way too simple and gloss over these details.

[1]: http://stackoverflow.com/a/34135166/

Re:Opinionated Comparison of React, Angular2, and Aurelia by batmansmk on Friday April 07, 2017 @11:29AM (#14060494)
It's 100% Redux ecosystem responsibility to perform the actions you want. I understand where you come from, as you perfectly described a procedure and it sounds natural to think that way. Redux and Event sourcing systems force you to decompose in 3 functions this procedure

- Action creator: the action creator newTodo() creates an action = {type: 'new', body = 'yo', date: new Date()}. This action creator can be colocated in the reducer file.

- Selectors: the summary is a denormalized view of the normalized state. Many would argue it is not a business logic but a display logic. Same for the sorting. A selector is a function that takes a part of the state as an input and return a new datastructure. Use reselect if you have perf concerns and want to persist it. It can be colocated code wise with the reducer file.

- Saga: for the side effects of sending an email, we use Redux Saga for ol' REST based endpoints. Those can be colocated code wise with the reducer file.

So yes you have 4 functions for your todo business logic: some action creators, some selectors, some sagas and the reducer. They can all be in the same file. It takes a bit of practice to figure out what is the right cut-off point. But all in all, interns and old farts like me all grasped it way more easily than Angular 1 scope concept for a more rewarding value.

Re:Opinionated Comparison of React, Angular2, and Aurelia by WA on Friday April 07, 2017 @04:51PM (#14063407)
This is helpful, thank you. One note:

> Selectors: the summary is a denormalized view of the normalized state. Many would argue it is not a business logic but a display logic. Same for the sorting.

Yes, I read about that before. Don't put in the store what can be computed. But in my case, this isn't used in a view, but in a separate "object" that can be shared with others without sharing the underlying data. So, while the report attributes are all computed, they must be stored somewhere for later individual retrieval.

Anyways, could you please clarify this:

> So yes you have 4 functions for your todo business logic

Where do I bundle these 4 functions so that they're called always in the same order?

> It takes a bit of practice to figure out what is the right cut-off point.

Yes, that's my struggle right now ;)

Re:Opinionated Comparison of React, Angular2, and Aurelia by BigJono on Friday April 07, 2017 @07:16AM (#14058759)
The common solution seems to be using redux-thunk to shoehorn all of this business logic into the action creators. I think that's an absolutely terrible idea. IMO redux-thunk muddies up the terms "dispatch" and "action creator" and essentially creates a huge noob trap where new programmers don't even know what an action creator is anymore and they use the wrong terminology for everything.

Having said that, I think the thunk pattern is a fantastic solution. The two things the business logic functions need to do their job are dispatch, and the state, and the way redux-thunk does it with dependency injection makes your business logic functions very easy to test.

If somebody can come up with a solution with the same principle but without the absolutely moronic overloading of terms that people somehow find acceptable about redux-thunk, then they'll be onto a winner.

Re:Opinionated Comparison of React, Angular2, and Aurelia by WA on Friday April 07, 2017 @08:28AM (#14059065)
Thanks for the input. This reflects what my problem is as a beginner with all this stuff: Where do I actually put everything? There's no universal answer of course, but this is why I (and certainly many others) feel intimidated by the JS ecosystem as a whole.
Re:Opinionated Comparison of React, Angular2, and Aurelia by acemarke on Friday April 07, 2017 @02:46PM (#14062427)
The Redux FAQ has a related entry: http://redux.js.org/docs/faq/CodeStructure.html#structure-bu... .

Out of curiosity, could you give me some examples of what you view as "business logic", and what troubles you're having placing that into a React+Redux app?

(Also worth noting that BigJono and I debated the merits of redux-thunk in another recent thread: https://news.ycombinator.com/item?id=13987613 ).

Re:Opinionated Comparison of React, Angular2, and Aurelia by WA on Friday April 07, 2017 @04:33PM (#14063283)
Thanks for the link. Business logic, to me, is: 1. Make sure that domain rules are enforced and keep data consistent. 2. For every use case, always trigger the same sequence of actions (behavior and side effects).

Imagine a fitness app where you can enter workouts and how much water you drank. You may enter only one workout per day (rule) and you cannot enter more than 1 gallon of water (rule). When you add a workout (behavior & side effects): the weather API is called and the current weather is added to the workout, the workout is stored in the DB, some calculated statistics are updated, your public "progress report card" is updated, you get a "good job!" push notification.

In my PHP OOP spaghetti approach, I might create an AddWorkoutCommand that executes() these actions by calling a bunch of other functions in other objects and trigger an event for optional listeners/actions. With Redux, not everything is in the same store (or in the store at all) and I don't even know where to start. It feels like this doesn't have anything to do with Redux, because it's partially not even state/view-related.

Maybe I'm overthinking this, but I do have a hard time finding a good tutorial that shows how to work with behavior and rules that goes beyond "add this item to the todo array".

Re:Opinionated Comparison of React, Angular2, and Aurelia by acemarke on Friday April 07, 2017 @06:58PM (#14064094)
I could picture handling that kind of thing several different ways.

First, the UI may want to prevent the user from being able to hit "Add" or something if a workout already exists for that day. The component could use a selector to determine if there's an existing workout, return the flag in `mapState`, and disable buttons or forms appropriately.

Second, the action creation logic could decide to not actually dispatch an `ADD_WORKOUT` action by inspecting the current state and only dispatching the action if allowed. (I have some relevant examples of redux-thunk use cases like this at [0]).

Third, the action could get always dispatched, but the reducer could make the decision whether to actually insert the proposed workout into state.

Regarding the side effects, this is a good use case for "sagas". Dispatch an `ADD_WORKOUT` action that triggers a saga, but is ignored by the reducers. The root saga kicks off a child saga when it sees that action. The workout saga could then decide whether it needs to actually add the workout based on state, dispatch another action that actually updates the state, make the AJAX call for weather and persist it to the server, and trigger a push notification. (You can do same thing in a thunk, it just would need explicit promise usage rather than the synchronous-looking code of a generator-based saga.)

Lastly, this isn't a direct answer to your question, but you may be interested in some of the more in-depth Redux tutorials I've collected. They're in the 'Redux Tutorials#Project-Based Tutorials" section of my links list [1]. In particular, I recommend "Building a Simple CRUD App with Redux" [2], and my own "Practical Redux" series [3].

Hopefully this gives you some ideas. I'd also invite you to come ask questions in the Reactiflux chat channels on Discord. The invite link is at https://www.reactiflux.com . Great place to hang out, learn, and discuss React + related topics.

[0] https://gist.github.com/markerikson/ea4d0a6ce56ee479fe8b356e...

[1] https://github.com/markerikson/react-redux-links/blob/master...

[2] http://www.thegreatcodeadventure.com/building-a-simple-crud-...

[3] http://blog.isquaredsoftware.com/series/practical-redux/

Re:Opinionated Comparison of React, Angular2, and Aurelia by BigJono on Friday April 07, 2017 @10:55PM (#14065027)
Just to be clear, I wasn't debating the merits of thunks, I love the pattern. I was just questioning the decision to use redux middleware to implement them. Redux-thunk action creators do not return an action. It's a misnomer to call them action creators.
Re:Opinionated Comparison of React, Angular2, and Aurelia by lobster_johnson on Friday April 07, 2017 @07:28PM (#14064247)
Redux reducers don't manage the state of the view, but of the app. Redux state is the "M" in MVC; Redux action creators are the "C".

The view's job is to display the current state, translating it into something presentable. Views call action creators to request changes to the state, or to report events that action creators should use to make changes to the state. Action creators implement business logic.

Edit: To be clear, this doesn't mean you have to do logic in action creators. You can keep action creators "pure" (not thunked), and instead have a business logic layer that mediated between view and action dispatching. Saga is one way of doing this. Redux doesn't mandate anything specific.

Reducers are a way to serialize the state so that all state transitions happen as a strictly sequential chain of modifications. This is done to prevent the spaghetti mess you get when (as in traditional MVC systems like Cocoa) everyone is allowed modify the state directly, and views merely observe state. By flowing state through a single bottleneck, there's only one moment when the UI needs to transition to a new state. With observation-based systems, it's completely unpredictable and certainly not reversible.

React components do of course also have something called "state", but you have to think of that as the UI state, i.e. things needed to manage the view, such as whether a dropdown is open or what the current text of an input is.

There's also the temptation to put UI-specific state in Redux. This is perfectly normal, and even desirable. The "M" in MVC is not some abstract ivory-tower notion that there exists some world state that's entirely independent of the view. The state serves the view; whatever the view needs must be provided by the state.