Skip to content
This repository has been archived by the owner on Mar 23, 2023. It is now read-only.

Question: Should actions know about stores? Should actions maintain state? Should I trigger actions from componentWillUpdate? #185

Closed
TimothyRHuertas opened this issue Mar 27, 2015 · 11 comments

Comments

@TimothyRHuertas
Copy link

In short. I am issuing an XHR. When it returns I need to trigger an action with input data from a Store. What is the preferred approach?

In long, consider the following scenario :)
I have a page that contains a list of search results. The search results and the query used to retrieve them are stored in a SearchStore. These search results are loaded via an XHR dispatched from SearchActionCreators. Users can edit items in the search results. When they do so I issue an XHR via EditActionCreators. When the xhr returns (eg the action is complete) I want to "refresh" the search results in the SearchStore using the current query in the SearchStore.

There are a few approaches I can take to accomplish this:

  1. Invoke SearchActionCreators.refresh(SearchStore.currentQuery) from within EditActionCreators when the XHR is complete. This approach requires my ActionCreator maintain a reference to my Store.
  2. Invoke SearchActionCreators.refresh(SearchActionCreators.currentQuery) from within EditActionCreators when the XHR is complete. This approach requires my ActionCreator to maintain state.
  3. Invoke SearchActionCreators.refresh(SearchStore.currentQuery) from componentWillUpdate within my view. This does not seem to fit with the intent of componentWillUpdate.

Flux is straight forward when XHR's result in DOM changes, but I start to get confused when XHR's result issuing other XHR's with input from the current state of the app (contained in a Store).

@luisherranz
Copy link

If you are storing the query in the SearchStore why do you need to send it again?

I think in your case I would register the SearchStore for the action dispatched when the edit returns. In that callback, I would update the Store data accordingly and send the change event (so the view refreshes itself).

@TimothyRHuertas
Copy link
Author

@luisherranz

Let me clarify what I mean what I say "and the query used to retrieve them are stored in a SearchStore":

I am consuming a search API; the parameters to that request {partnerName:string, startDate:Date} are stored in the SearchStore.

I need to send the query again (via an action) to refresh (via SearchActionCreators.refresh) the search results after a user performs an edit (via EditActionCreators).

I'm not sure how to get query parameters stored in SearchStore to SearchActionCreators.refresh.

@sterpe
Copy link

sterpe commented Mar 27, 2015

Why not pass the query to the action creator via the component onclick?

I.e:
onClick: function(){
Actioncreator.refresh(store.query);
}

Presumably the component should be able to access the store already.

But also I don't think there is any conceptual problem with an action creator keeping a reference to a store to read data so long as any writes to the store are done as regular dispatches and not as some type of special non flux relationship.

Sometimes it makes sense that there would be a store with raw data that is used to populate a lower store with a version of that data specific to a components needs. In this case I have an action creator that maintains a ref to the more abstract data store and reloads the dependent store via a dispatch. The component only knows about the dependent store so the action creator maintains a handle to the main store itself.

Sent from my iPhone

On Mar 27, 2015, at 6:26 AM, TimothyRHuertas notifications@github.com wrote:

@luisherranz

Let me clarify what I mean what I say "and the query used to retrieve them are stored in a SearchStore":

I am consuming a search API; the parameters to that request {partnerName:string, startDate:Date} are stored in the SearchStore.

I need to send the query again (via an action) to refresh (via SearchActionCreators.refresh) the search results after a user performs an edit (via EditActionCreators).

I'm not sure how to get query parameters stored in SearchStore to SearchActionCreators.refresh.


Reply to this email directly or view it on GitHub.

@TimothyRHuertas
Copy link
Author

@sterpe your suggestions are pretty pragmatic; I like them. I wasn't sure about the action creators maintaining a reference to the store because the architecture diagram doesn't suggest that. Passing the query parameters to the edit action seems like the right thing to do. Perhaps I should change the name of the method to editAndReload for the sake of semantics. Thanks for the suggestion. I'm going to leave this open for another day or two to see if others agree.

@fisherwebdev
Copy link
Contributor

I think the missing piece here is a WebAPIUtils module. This module has access to all the stores and knows how to construct a query to the Web API. The call to the Web API is based on the current values it can retrieve from the stores through the stores' getter methods.

Here's the sequence:

  1. User performs edit.
  2. View component calls action creator (with edited text?).
    SearchActionCreators.refresh()
  3. The action creator calls the WebAPIUtils module after dispatching the action.
    WebAPIUtils.refresh()
  4. The WebAPIUtils module gets data from the stores that it needs to make the call to the server.
    var query = SearchStore.getCurrentQuery()
  5. WebAPIUtils initiates server call and in turn calls an action creator when it handles the response.
// XHR using something like jQuery
$.ajax({
  url: mySearchAPIEndpoint,
  data: query,
  success: SearchActionCreators.refreshSucceeded,   
  error: SearchActionCreators.refreshErrored,
});

I want to clarify some things mentioned above, however.

Other than the controller-views, the view components should not have references to the stores. If you need a value in the views, you should be passing that value down through props.

Actions (objects) should report on what has just happened in the "real" world (the server responded in a certain way, the user acted upon the UI in a certain way, a requestAnimationFrame has just cycled, etc).

Action creators (methods) should not be responsible for maintaining state or passing unchanged values around the application. Instead, they just construct an action and pass it to the dispatcher. They can also call out to a WebAPIUtils module to initiate a server call as an additional side effect.

The action should be as complete of a picture as possible as to what just happened. Then the stores take over and decide what to do about it. If the stores need to know information from a different store, they can call that store's getters after a call to Dispatcher.waitFor(), ensuring that they will receive the latest data.

@TimothyRHuertas
Copy link
Author

@fisherwebdev You are correct. The missing link is web api util's having knowledge of stores. I was under the impression that they were intended to be plain old web clients. Things make sense now.

Thanks for clarifying how store data gets passed to components. I would add that for controller views that composite a large amount of components (e.g. data grids) understanding the PureRenderMixin and immutability is key to avoiding performance problems. Our team struggled with this in the beginning.

@whichsteveyp
Copy link

Thanks for the reply here @fisherwebdev, the clarifications at the end help a lot with some other stuff I've been trying to sort through. I haven't run in to the issue Tim has yet, but I'm building a similar project in parallel and I'm bound to eventually.

Right now my stores are not taking advantage of Dispatcher.waitFor() so I'll try to dig in to that a bit more.

@TimothyRHuertas
Copy link
Author

Closing this out. Thanks again all for your help. It really helped move my code forward.

@techird
Copy link

techird commented Jan 12, 2016

@fisherwebdev I still have a question with the WebAPIUtils Module

Here's the sequence:

  1. User performs edit.
  2. View component calls action creator (with edited text?).
    SearchActionCreators.refresh()
  3. The action creator calls the WebAPIUtils module after dispatching the action.
    WebAPIUtils.refresh()
  4. The WebAPIUtils module gets data from the stores that it needs to make the call to the server.
    var query = SearchStore.getCurrentQuery()
  5. WebAPIUtils initiates server call and in turn calls an action creator when it handles the response.

The SearchActionCreator can call the WebAPIUtils.refresh() means the WebAPIUtils module may have a dependency of action creators. And when the WebAPIUtils take action creator's method as xhr callback, it means the action creator have a dependency of WebAPIUtils module.

Why this comes into in a dependency cycle?

@TimothyRHuertas
Copy link
Author

I don't think WebAPIUtils should depend on SearchActionCreator. Rather have WebAPIUtils.refresh dispatch a complete event (or return a promise, or take a callback) that SearchActionCreator listens to. When SearchActionCreator is notified that WebAPIUtils.refresh is complete it can create a new action (REFRESH_COMPLETE or REFRESH_ERROR).

That said if you are creating a new project you should consider using Redux. There are some nice tutorial videos linked to from the readme https://github.com/rackt/redux. This video explains how Flux inspired redux https://www.youtube.com/watch?v=xsSnOQynTHs

@techird
Copy link

techird commented Jan 14, 2016

Thanks @TimothyRHuertas , promises seems to be a better solution. Heard of redux for a while, will take a look into it in a couple days.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants