GithubHelp home page GithubHelp logo

Comments (8)

lwhorton avatar lwhorton commented on September 7, 2024 3

Thanks for the quick response time @gaearon. I think the conceptual problem I'm having was clarified by this:

You need to put your side effects somewhere. We suggest to dispatch “async actions” which are handled by “middleware” and turn into proper “actions” at some point. You may do side effects differently—it's up to you. But we find this mechanism is convenient.

If I were the architect, I would move these side effects into some other contraption that is not middleware. Middleware (to me) seems best suited to orthogonal devices such as logging, error handling, and other interceptions, and should not be responsible for any domain logic or feature-related side effects. Given that you have only one opportunity when initializing a redux app to create a custom dispatcher via createDispatcher(composeStores(stores), [middlewareA, middlewareB, ...]), it seems an awkward place to attach anything but app-wide orthogonal concerns. I'm just imagining a larger app where side-effects are all handled by middleware, and your bootstrap file for creating the flux app is this giant blob of [middlewareForFeatureA, secondMiddlewareForFeatureA, middlewareFeatureB, SecondMiddlewareFeatureB, ThirdMiddlewareFeatureB, ...] which seems at the very least a huge nuisance.

In order to take advantage of the event-sourced architecture of redux we need simple object actions pumped through our reducers. Generating these actions via middleware seems convoluted for reasons listed above. Perhaps there can be a second mechanism for handling a logic-laden (or async) action before it ultimately dispatches a synchronous, simple action object. For sake of the example let's call this the domainer. Imagine you have a view component trigger addTodo(text), but you have the added domain requirements of 1) no more than 3 todos at once, and 2) a spell-checker that connects to an autocorrect service via AJAX before adding your todo to the list. The view component (directly or indirectly) invokes addTodo(text) via the domainer. This domainer ensures we have fewer than 3 todos, then fires off the ajax spellchecker service. Exactly like the provided middleware examples, it then dispatches a success/error action with the spell-checked todo text. Really the only difference here is a distinct encapsulation of domain logic / async handling into a single unit versus various middleware behaviors.

Now that I have typed this all out and waded through my own thoughts, I think what I'm missing in Redux is a dedicated state machine. Reducers provide simplicity from (state, action) -> newState, but in reality those switch statements are just begging for a dedicated state machine implementation. The domain logic hidden behind switch statements and if/then blocks could be much more explicitly stated in the form of states: { 'waitingForTodo', 'addingTodo', 'maximumTodos', 'noTodos' } where each state has different acceptable inputs: waitingForTodo: -addTodo(text) -removeTodo(index) -removeAllTodos() etc.

This is all crazy talk and hypotheticals, but I appreciate your feedback. I think I should stop trying to impose my design thoughts onto a differently laid out architecture. Feel free to close this as not really an issue, more of a discussion leading nowhere.

from redux-thunk.

gaearon avatar gaearon commented on September 7, 2024 1

Disregarding the “component receives action” misconception, what you describe is convenient to implement with redux-thunk:

function addTodoWithoutCheck(text) {
  return {
    type: 'ADD_TODO',
    text
  };
}

export function warnTooManyTodos() {
  return {
    type: 'WARN_USER',
    message: 'Too many todos.'
  };
  };
}

export function addTodo(text) {
  return function (dispatch, getState) {
    return (getState().todos.length === 3) ?
      dispatch(warnTooManyTodos()) :
      dispatch(addTodoWithoutCheck(text));      
    }
  }
}

Yes, this is domain logic, but it's not “mixed” with actions. It is interpreted by middleware (in this case, redux-thunk). You still get plain actions in the reducers.

You could move it somewhere and call it something else, but it's just too convenient to be able to “dispatch” these things, whether you call them “async actions”, “instructions”, or something else.

from redux-thunk.

gaearon avatar gaearon commented on September 7, 2024 1

Finally, yes, you should strive to put your logic into reducers whenever possible. But sometimes it's just too much pain, or involves side effects. That's when you'd use redux-thunk.

from redux-thunk.

gaearon avatar gaearon commented on September 7, 2024

Maybe this particular example isn't good. We usually use redux-thunk for side effects, e.g. async API calls. You need to put those side effects somewhere.

By the time action is plain object and reaches a reducer, it's just a description of what happened. We found it useful to also be able to express intention when dispatching, even if such “async action” transforms into side effects and produces one or more actions, potentially asynchronously, later.

You need to put your side effects somewhere. We suggest to dispatch “async actions” which are handled by “middleware” and turn into proper “actions” at some point. You may do side effects differently—it's up to you. But we find this mechanism is convenient.

Does this clarify anything, or do you still disagree? Please propose how to express http://gaearon.github.io/redux/docs/recipes/ReducingBoilerplate.html#async-action-creators in a clean manner without the concept of “async actions” and a helper like redux-thunk?

from redux-thunk.

gaearon avatar gaearon commented on September 7, 2024

Perhaps one component really wants to know each and every time a new todo is added (maybe for displaying a "too many todos" error) while a second component will receive the action, but follows its own logic to disregard that action because it reads that there are too many todos already.

I'm not sure what you mean by “component receiving action”. Components can't receive actions. They only receive the current state. Reducers receive actions.

from redux-thunk.

gaearon avatar gaearon commented on September 7, 2024

That's an interesting discussion, and I'd appreciate if you could whip up a proof of concept of your proposed system for side effects and created an issue in the main Redux repo. We're not dogmatic, it's just that this is the best we have at the moment.

from redux-thunk.

gaearon avatar gaearon commented on September 7, 2024

Related:

reduxjs/redux#351
reduxjs/redux#343
reduxjs/redux#307

But at this point, indeed, discussion without a proof of concept goes nowhere.

from redux-thunk.

gaearon avatar gaearon commented on September 7, 2024

You might be interested in Redux Saga project which moves side effects to generator-driven "sagas": reduxjs/redux#1139

from redux-thunk.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.