Reputation: 6958
I have what I believe is a very common scenario... I'm building a dashboard of components that will be driven by some datasource. At the top of the view would be a series of filters (e.g. a date range). When the date range is updated, the components on the screen would need to update their data based on the selected range. This would in turn force the individual components that are slave to that picker to need to fetch new data (async action/XHR) based on the newly selected range.
There can be many components on the screen and the user may wish to add/remove available displays, so it is not as simple as always refreshing the data for all components because they may or may not be present.
One way I thought to handle this was in the action dispatched when a new date range is selected was to figure out what components are on screen (derived from the Store) and dispatch async actions to fetch the data for those components. This seems like a lot of work will go into the DATE_CHANGED action.
Another alternative might be to detect date range changes in store.subscribe() callbacks from each of the components. This seems to decouple the logic to fetch the data from the action that caused this to happen. However, I thought it was bad practice (or even an error) to dispatch while dispatching. Sure I can wrap it in a setTimeout, but that feels wrong too.
Third thing that came to mind was just doing fetch calls directly in the component's store.subscribe() and dispatching when those return, but I thought this breaks the connect model.
This seems like a common pattern to fetch based on state changes, but I don't know where its best to put those. Any good documentation / examples on the above problem?
Upvotes: 0
Views: 320
Reputation: 2925
Don't use store.subscribe
for this. When DATE_CHANGED
reaches the reducer it's meant for, simply change the application state (I'm assuming the date range is part of the store somehow). So you have something like state.rangeStart
and state.rangeEnd
.
You didn't mention what view rendering library you're using, so I can only describe how this is typically done with React:
state.rangeStart
or state.rangeEnd
changed.componentWillReceiveProps
or getDerivedStateFromProps
in the newest release). In this handler you dispatch async redux actions that fetch the data the component needs. Your view library will probably have something similar.DATE_CHANGED
action. For example, if state.listOfThings
(an array) entirely depends on the date range, you would set it to an empty array as soon as the date changes: return { ...state, listOfThings: [] }
. This causes the components to display that data is being fetched again.REQUEST
-> SUCCESS
/FAILURE
cycle and have populated the store with the data, connected components will automatically render it. This is kind of its own chapter, look into redux async actions
if you need more information.state.listOfThings
for the current date range, you don't want to fetch this data twice. So there needs to be a way to detected that 1) the data range has changed but also 2) a request to fetch listOfThings
is already on its way. This is usually done with boolean flags in the state: state.isFetchingListOfThings
. The async actions fetching this data cause the reducer to set this flag to true. Your components need to be aware of this and dispatch actions conditionally: if (props.rangeStart !== nextProps.rangeStart && !nextProps.isFetchingListOfThings) { props.fetchListOfThings(); }
.Upvotes: 1