score:151
i've seen the flux pattern implemented both ways, and after having done both myself (initially going with the former approach), i believe that stores should be dumb recipients of data from the actions, and that asynchronous processing of writes should live in the action creators. (async reads can be handled differently.) in my experience, this has a few benefits, in order of importance:
your stores become completely synchronous. this makes your store logic much easier to follow and very easy to test—just instantiate a store with some given state, send it an action, and check to see if the state changed as expected. furthermore, one of the core concepts in flux is to prevent cascading dispatches and to prevent multiple dispatches at once; this is very difficult to do when your stores do asynchronous processing.
all action dispatches happen from the action creators. if you handle asynchronous operations in your stores and you want to keep your stores' action handlers synchronous (and you should in order to get the flux single-dispatch guarantees), your stores will need to fire additional success and fail actions in response to asynchronous processing. putting these dispatches in the action creators instead helps separate the jobs of the action creators and the stores; furthermore, you don't have to go digging through your store logic to figure out where actions are being dispatched from. a typical asynchronous action in this case might look something like this (change the syntax of the
dispatch
calls based on the flavor of flux you're using):someactioncreator: function(userid) { // dispatch an action now so that stores that want // to optimistically update their state can do so. dispatch("some_action", {userid: userid}); // this example uses promises, but you can use node-style // callbacks or whatever you want for error handling. somedataaccesslayer.dosomething(userid) .then(function(newdata) { // stores that optimistically updated may not do anything // with a "success" action, but you might e.g. stop showing // a loading indicator, etc. dispatch("some_action_success", {userid: userid, newdata: newdata}); }, function(error) { // stores can roll back by watching for the error case. dispatch("some_action_fail", {userid: userid, error: error}); }); }
logic that may otherwise be duplicated across various actions should be extracted into a separate module; in this example, that module would be
somedataaccesslayer
, which handles doing the actual ajax request.you need less action creators. this is less of a big deal, but nice to have. as mentioned in #2, if your stores have synchronous action dispatch handling (and they should), you'll need to fire extra actions to handle the results of asynchronous operations. doing the dispatches in the action creators means that a single action creator can dispatch all three action types by handling the result of the asynchronous data access itself.
score:1
if you want one day to have a development environment comparable to what you see in bret victor's famous video inventing on principle, you should rather use dumb stores that are just a projection of actions/events inside a data structure, without any side effect. it would also help if your stores were actually member of the same global immutable data structure, like in redux.
more explainations here: https://stackoverflow.com/a/31388262/82609
score:2
gaeron's flux-react-router-demo has a nice utility variation of the 'correct' approach.
an actioncreator generates a promise from an external api service, and then passes the promise and three action constants to a dispatchasync
function in a proxy/extended dispatcher. dispatchasync
will always dispatch the first action e.g. 'get_external_data' and once the promise returns it will dispatch either 'get_external_data_success' or 'get_external_data_error'.
score:3
i'll provide an argument in favor of "dumb" actions.
by placing the responsibility for collecting view data in your actions, you couple your actions to the data requirements of your views.
in contrast, generic actions, that declaratively describe the intent of the user, or some state transition in your application, allows any store that responds to that action to transform the intent, into state tailored specifically for the views subscribed to it.
this lends itself to more numerous, but smaller, more specialized stores. i argue for this style because
- this gives you more flexibility in how views consume store data
- "smart" stores, specialized for the views that consume them, will be smaller and less coupled for complex apps, than "smart" actions, on which potentially many views depend
the purpose of a store is to provide data to views. the name "action" suggests to me that its purpose is to describe a change in my application.
suppose you have to add a widget to an existing dashboard view, which shows some fancy new aggregate data your backend team just rolled out.
with "smart" actions, you might need to change your "refresh-dashboard" action, to consume the new api. however, "refreshing the dashboard" in an abstract sense has not changed. the data requirements of your views is what has changed.
with "dumb" actions, you might add a new store for the new widget to consume, and set it up so that when it receives the "refresh-dashboard" action type, it sends a request for the new data, and exposes it to the new widget once it's ready. it makes sense to me that when the view layer needs more or different data, the things that i change are the sources of that data: stores.
score:8
the stores should do everything, including fetching data, and signalling to components that the store's data has been updated. why? because actions can then be lightweight, disposable and replaceable without influencing important behavior. all important behavior and functionality happen in the store. this also prevents duplication of behavior that would otherwise be copied in two very similar but different actions. the stores are your single source of (handling the) truth.
in every flux implementation i've seen actions are basically event strings turned into objects, like traditionally you'd have an event named "anchor:clicked" but in flux it would be defined as anchoractions.clicked. they're even so "dumb" that most implementations have separate dispatcher objects to actually dispatch the events to the stores that are listening.
personally i like reflux' implementation of flux where there are no separate dispatcher objects and action objects do the dispatching themselves.
edit: facebook's flux actually fetches in "action creators" so they do use smart actions. they do also prepare the payload using the stores:
https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/actions/chatmessageactioncreators.js#l27 (line 27 and 28)
the callback on completion would then trigger a new action this time with the fetched data as payload:
so i guess that's the better solution.
score:51
i tweeted this question to the devs at facebook and the answer i got from bill fisher was:
when responding to a user's interaction with the ui, i would make the async call in the action creator methods.
but when you have a ticker or some other non-human driver, a call from the store works better.
the important thing is to create an action in the error/success callback so data always originates with actions
Source: stackoverflow.com
Related Query
- Should flux stores, or actions (or both) touch external services?
- Should stores call actions in reactjs flux?
- Where should I dispatch actions to call services to retrieve data needed before initial app load?
- Where should ajax request be made in Flux app?
- What is the difference between .ts and .tsx extensions. Both are used as extensions for typescript files in react. So where should we use them?
- At what nesting level should components read entities from Stores in Flux?
- Flux best practices: Stores dispatching actions, AJAX calls in Web API Utils?
- React + Flux - should store data be stored in a component state, or props?
- How to handle one-to-many relationships in Flux stores
- Which should be simpler, actions or reducers?
- Do react native uses same code for ios and android or both platform code should be written separately?
- Why should I use Actions in Flux?
- Flux calling actions with arguments managed in store
- How to handle multiple stores of same type in Flux / ReactJS?
- Where should you put the git hub workflow directory for actions in a full-stack project?
- Can flux actions access stores?
- Flux + React.js - Callback in actions is good or bad?
- Questions on State vs. Flux Stores for multi-feature application
- Why I should set redux actions type with an exported constants?
- Should Reacts/Flux' stores be a snapshot of the whole state of a GUI?
- Reflux stores not listening to actions
- How to properly chain actions in Flux (alt)
- How to fire Flux actions for RelayMutations
- Testing flux stores with mocha, chai and sinon
- Flux actions for UI events
- React.js + Flux - Init data objects in stores properly
- fluxxor add more than 1 set of actions to a flux instance
- Flux actions getting captured by wrong handlers on this very simple dummy app, Why?
- How to implement the actions queue in a flux app
- Loading both inline and external scripts with Helmet
More Query from same tag
- Add accessibility to React-leaflet map
- React project in Visual Studio 2017
- How to filter based on 3 different conditions (ReactJS)
- How to test select option logic with React Testing Library
- How to set background image for entire screen in app js file in react js?
- Why useContext does't work in my function?
- Relation between import and destructing es6 syntax?
- How should I update a component and non-component from an AJAX call?
- How can I thicken the underline on a MaterialUI <Link>?
- error: line 8 parsing error unexpected token
- React component disappears after one use
- how to use history instance while using react routers v6?
- React-window loses focus
- Creating a line with an arrow in React
- Custom hook that updates all components that use it in sync
- React Hooks stale state inside function
- Set State in input type without callback function
- NavLink/Link from React-router-dom not working (blank screen)
- Testing Redux Enzyme: Component doesn't re-render after state has changed
- Why does the animation resets after the dropdown is shown in react-transition-group?
- SVG does not render - React
- List Rendering in React JS
- React - Dynamic background colors to parent and child
- How to link to section in react toggle menu
- Warning: Each child in a list should have a unique "key" prop - I don't need to loop over this
- Union type doesn't allow if statement with Typescript and React
- React JS .map and assignment causing esLint error 'Assignment to property of function parameter. eslint(no param-reassign)'
- Debounce library hook of a lib that make API call on React
- Search Through Array, Return Results
- installing React bootstrap react in react. Object not found,