Initialize part of Redux store on demand - redux

I'm migrating my Reflux based application to the Redux and came across one issue. I have many stores in the application (which is a good or bad thing about Reflux) and some of them get initialized only when a certain page trying to use the store. Basically, the store initialization happens then a component connected to the store is about to mount. When initialising the store I actually loading the data asynchronously.
To illustrate the current behavior:
Suppose I have 5 pages and 5 stores, where each page using one store. The data for initial store state loaded only when the user navigates to the respective page. So, on initial application load, I only load data for store1 and only when user navigating to the page2 I'll load data for store2.
Now, I replacing my Reflux stores with a single Redux store which is consist of 5 parts and I'm planning to implement reducers for each of the parts. Every page if going to be associated with a reducer (page1 -> reducer1, page2->reducer2 etc.). From my understanding of how Redux is supposed to work, each parts of the store will be initialized by a respective reducer and it all will happen at the store initialization time. In the real application, I have, 50 stores, and I assume it will results in 50 API calls immediately at the application initialization which is not good at all. Not all of that data is required for the initial page.
Therefore my question: Is there any way to load the initial store state on demand in Redux? Or how could it be approached?
I understand, that I can fire an action to load data into the store before navigating to the page, but in reality, pages connected to several store parts and it is not easy to figure out what actions I need to call (although it is possible).
A similar question has been asked here but my question more about how to not load everything on initialisation.

Yes you can.
When you create your reducer, it is an good idea to give the state argument an default value so that when it is never undefined.
When you first initialize your store, you have the opportunity to pass down an initialState object. So you can do something like this:
let initialState;
if (yourDemand) { // load the intialState on 'yourDemand'
initialState = {
page1: intial state for page1Reducer
page2: intial state for page2Reducer
page3: intial state for page3Reducer
}
}
const store = createStore(
reducers,
initialState
);

Related

Redux / Flux Pattern for Fetching Data When Store Updates

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?
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:
The components know wether they are currently mounted (visible) or not, so redux doesn't need to be concerned with that. What you need is a way to detect that state.rangeStart or state.rangeEnd changed.
In React there is a lifecycle hook for that (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.
The components display some kind of "empty" or "loading" state while you're waiting for the new data typically. So a good practice is to invalidate/clear data from the store in the reducer that handles the 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.
When all the async redux actions went through the 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.
The tricky part are interdependencies between the components and the application they're rendering. If two different dashboard components for example want to fetch and render 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(); }.

Handling dependencies in Redux Store data

My team and I are busy designing a redux store, including all the possible actions that can be fired. Due to the nature of our application, we have some inherent coupling/dependencies between different nodes (branches?) of our redux store. Our idea of tackling these dependencies in the most scalable manner was to extract it to its own separate node in the state tree. Let me give a simplified example to illustrate the structure we have in mind, and get to the problem that we're facing:
Say our state tree has the following:
SectionA: with data A as a bool
SectionB: with data B as an int
DependencySection: with a dependency that if A is true then B has to be 10 or larger
You're probably thinking, why do it this way? Why not just integrate the dependency into the reducer for SectionB? The reason is that these dependencies vary per client, and we're reading them from a database. It can link any part of the state tree to any other part and have its own actions that need to happen.
Now, my question is, how do we reduce actions while taking into account these dependencies? If our application flow is:
User causes TOGGLE_DATA_A action
SectionA reducer updates data A
DependencySection updates data B based on the dependency
What happens if we introduce a SectionC, with data C that is dependent on data B? It seems like a new action needs to be fired when the DependencySection updates data B, with the action being that data B has been updated. This would mean firing an action while another action is being reduced, which is obviously not allowed. Alternatively, it seems like making the different reducer sections execute in a VERY specific order would also solve the issue, but this is surely an anti-pattern.
The only solution we can come up with is to have middleware that repeatedly fires UPDATE actions after every action until the state no longer changes. For example, after the TOGGLE_DATA_A action updates data B (via the dependency on data A), the next UPDATE action would update data C (via the dependency on data B), and the next UPDATE would update nothing, stopping the loop. This is quite hacky.
So, is there a better way to deal with reducing such a dependent state? Or should we be structuring our state tree differently?
One of the key concepts behind Redux is that reducer logic is just functions, and if you need to order your state update handling in a specific order, you can do that yourself by writing explicit code for that. So no, "making different reducer sections execute in a specific order" is not an "anti-pattern", it's absolutely a valid and encouraged approach with Redux.
There's examples and discussions of this approach in the Structuring Reducers - Beyond combineReducers section of the Redux docs, and in my blog posts Idiomatic Redux: The Tao of Redux, Part 1 - Implementation and Intent and Practical Redux, Part 7: Feature Reducers. I'll paste in a basic hypothetical example:
export function commentsReducer(state = initialState, action, hasPostReallyBeenAdded) {}
// elsewhere
export default function rootReducer(state = initialState, action) {
const postState = postsReducer(state.post, action);
const {hasPostReallyBeenAdded} = postState;
const commentState = commentsReducer(state.comments, action, hasPostReallyBeenAdded);
return { post : postState, comments : commentState };
}

ngrx, How to have a starting state from an api?

I have my reducer with a starting state of an empty array:
folderReducer(state:Array<Folder> = [], action: Action)
I'd like to populate the starting state, so when I do
store.subscribe(s => ..)
The first item I get comes from the database. I assume the way of doing this is with ngrx/effects, but I'm not sure how.
Your store always has the initial state, that you define in the reducer-function. The initial states main purpose is to ensure that the application is able to start up and not run into any null-pointer-exceptions. And also it sets up your application to start making the first api-calls ect. - so you can think of it as a technical initial state.
If you want to fill your store with api-data on the startup, you would do that on the same way that you add/modify data during any other action - just that the action of "initially loading data" is not triggered by some user-interaction but through:
either when your root-component loads
or as part of a service in the constructor
In case you want to prevent specific components from showing anything until your API-call is done, you would have to adjust the display-components to display or hide data based on your state (e.g. by implementing a flag in your satet initialDataLoaded).
A dynamic initial state is now supported, see: https://github.com/ngrx/platform/blob/master/docs/store/api.md#initial-state-and-ahead-of-time-aot-compilation
Also see: https://github.com/ngrx/platform/issues/51
I would only do this if the database is local, otherwise the request will hold up loading of the application.

Where to keep "currentUser" globally accessible

Almost every object in my state tree needs to have the following fields:
createdByGuid
createdAt
modifiedAt
The date fields are easy to add in the action creators. There seems to be consensus that action creators are the place where impureness are allowed, i.e. new Date().
But the currently logged in user is not easily fetched since this data lives in another slice of the state.
Example state:
profile
data
guid
firstname
...
media
items
0: guid, createdByGuid, ...
1: guid, createdByGuid, ...
drawings
items
...
When a MEDIA_ADD action is dispatched I want the action creator to augment the dispatched data with createdById. Current user needs to be fetched from profile.data.guid.
I don't want to bring the state into my action creator. Instead I can use a thunk to get hold of the state via getState(). But this seems to be a clumsy way of getting hold of a simple guid constant (constant for this session). It becomes more clumsy when essentially all of my action creators for adding data to the state needs to be thunks.
I'm inclined to create a separate global singleton object to hold this data and bring this dependency into all of my action creator modules. This seems to be a lot less disturbing dependency in terms of for example testing.
I suppose most Redux users has about the same question regarding current user. What is a good solution?
I'm working in react-native, but I suppose the question applies to most Redux applications.
If someone is interested, I went the singleton way with this. Not a true singleton per definition, but it suits my needs.
When a user logs in I handle this in an asynchronous action creator (a thunk). The thunk calls setGlobalCurrentUser() after successfully authenticating the user with the server.
To get hold of the current user I only have to import currentUserGuid() from currentUser.js and I don't end up in a dependency hell.
currentUser.js:
let _currentUserGuid;
export function currentUserGuid() {
if(!_currentUserGuid) throw new Error('currentUserGuid:: No logged in user')
return _currentUserGuid;
}
export function setGlobalCurrentUser(user) {
if(user) {
_currentUserGuid = user.get('guid')
} else {
_currentUserGuid = null
}
}

How to handle cross-cutting concerns in redux reducers and actions

Given a use case like the one in this question:
Best way to update related state fields with split reducers?
What is the best practice for dealing with actions in reducers that depend on state outside of their own state? The author of the question above ended up just passing the entire state tree as a third argument to every reducer. This seems heavy-handed and risky. The Redux FAQ lists the following potential solutions:
If a reducer needs to know data from another slice of state, the state tree shape may need to be reorganized so that a single reducer is handling more of the data.
You may need to write some custom functions for handling some of these actions. This may require replacing combineReducers with your own top-level reducer function.
You can also use a utility such as reduce-reducers to run combineReducers to handle most actions, but also run a more specialized reducer for specific actions that cross state slices.
Async action creators such as redux-thunk have access to the entire state through getState(). An action creator can retrieve additional data from the state and put it in an action, so that each reducer has enough information to update its own state slice.
In my use case, I have an action "continue" that determines what page a user is allowed to go to in a multiple-form / multi-step process, and since this depends on pretty much the entire app state, I can't handle it in any of my child reducers. For now, I've pulled the store into the action creator. I use the current state of the store to calculate an action object that fires to my "page" reducer, which changes the active page. I will probably install redux-thunk and use getState() in this action creator, but I'm not committed to this approach yet.
I guess this isn't too bad of a solution since there is only one action (so far) that must be handled this way. I'm just wondering if there is a better solution, or if there is a way to re-structure my state and reducers to make it easier, or if what I'm doing is within best practices for Redux. If there are any similar examples out there, that would be helpful also.
To give some more context, my state tree currently looks like this:
{
order: order.result,
items: order.entities.items,
activePage: {
id: 'fulfillment'
// page info
},
pagesById: { // all the possible pages
fulfillment: {
id: 'fulfillment'
// page info
}
}
}
The active page is the page / section in which the user must enter data in order to proceed to the next page). Determining the active page almost always depends on the items state and sometimes depends on order state. The end result is an app where the user fills out a few forms in succession, hitting continue once the form is valid. On continue the app determines the next page needed and displays it, and so on.
EDIT: We've tried the approach of implementing a "global" reducer in combination with child reducers.
The implementation is like this...
const global = (currentState = initialState, action) => {
switch (action.type) {
default:
return currentState
}
}
const subReducers = combineReducers({
order,
meta
})
export default function (currentState = initialState, action) {
var nextState = global(currentState, action)
return subReducers(nextState, action)
}
The global reducer is first run on the whole app state, then the result of that is fed to the child reducers. I like the fact that I'm no longer putting a bunch of logic in action creators just to read different parts of state.
I believe this is in alignment with the principles of redux since every action still hits every reducer, and the order in which reducers are called is always the same. Any thoughts on this implementation?
EDIT: We are now using router libraries to handle the page state, so activePage and pagesById are gone.
If state.activePage depends of state.order and state.items, you may subscribe to the store and in case of modifications on "order" or "items" then dispatch a "checkPage" action which can set another active page if necessary. One way should to connect on a "top component" order and items, listen their values and change active page/redirect
Not easy to understand your concern, I hope my message will help. Good luck

Resources