I'm building an application, where I need to preload people
and planet
data (it's likely that in the future more preload requirements may be added) on launch of the application. I want to have value in the store that represents the global state of the app as loaded: <boolean>
. The value would be true only then when the preload requirements people.loaded: true
and planet.loaded: true
are true. The store would look something like this:
Store
├── loaded: <Boolean>
├── people:
│ ├── loaded: <Boolean>
│ └── items: []
├── planets:
│ ├── loaded: <Boolean>
│ └── items: []
Separate action creators make the needed async requests and dispatch actions which are handled by the People
and Planets
reducers. As shown below (uses redux-thunk):
actions/index.js
import * as types from '../constants/action-types';
import {getPeople, getPlanets} from '../util/swapi';
export function loadPeople () {
return (dispatch) => {
return getPeople()
.then((people) => dispatch(addPeople(people)));
};
}
export function loadPlanets () {
return (dispatch) => {
return getPlanets()
.then((planets) => dispatch(addPeople(planets)));
};
}
export function addPeople (people) {
return {type: types.ADD_PEOPLE, people};
}
export function addPlanets (planets) {
return {type: types.ADD_PLANETS, planets};
}
export function initApp () {
return (dispatch) => {
loadPeople()(dispatch);
loadPlanets()(dispatch);
};
}
../util/swapi
handles fetching people and planet data either from LocalStorage or making a request.
initApp()
action creator calls other action creators within site.js
just before rendering to DOM as shown below:
site.js
import React from 'react';
import {render} from 'react-dom';
import Root from './containers/root';
import configureStore from './store/configure-store';
import {initApp} from './actions';
const store = configureStore();
// preload data
store.dispatch(initApp());
render(
<Root store={store} />,
document.querySelector('#root')
);
1. What are the best practices for managing global preload state of the application in Redux?
2. Is having a global loaded
state in the store necessary?
3. What would be a scalable way of checking app loaded
state in multiple React components? It doesn't seem right to include People
and Planet
state for containers that just needs to know the global app state and doesn't handle rendering of People
or Planets
. Also that would be painful to manage when the global loaded
state would be needed in multiple containers.
Quoting part of Dan's answer from Redux - multiple stores, why not? question.
Using reducer composition makes it easy to implement "dependent updates" a la waitFor in Flux by writing a reducer manually calling other reducers with additional information and in a specific order.
4. Does Dan by calling other reducers mean calling nested reducers?
There is no “right” answer for this. Some users prefer to keep every single piece of data in Redux, to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state, such as “is this dropdown currently open”, inside a component's internal state.
Yes it can be easily manipulated.
For the most part if you have a react-redux app I would avoid local state entirely unless you can trying to solve a very component specific problem. For example I would use local state if my component does something on a setInterval and so it is keeping its own timer.
First, let me correct your example.
Instead of
export function initApp () {
return (dispatch) => {
loadPeople()(dispatch);
loadPlanets()(dispatch);
};
}
you can (and should) write
export function initApp () {
return (dispatch) => {
dispatch(loadPeople());
dispatch(loadPlanets());
};
}
You don’t need to pass dispatch
as an argument—thunk middleware takes care of this.
Of course technically your code is valid, but I think my suggestion reads easier.
- What are the best practices for managing global preload state of the application in Redux?
What you’re doing seems correct. There are no specific best practices.
- Is having a global loaded state in the store necessary?
No. As David notes in his answer, you’re better off storing only necessary state.
- What would be a scalable way of checking app loaded state in multiple React components? It doesn't seem right to include People and Planet state for containers that just needs to know the global app state and doesn't handle rendering of People or Planets. Also that would be painful to manage when the global loaded state would be needed in multiple containers.
If you’re concerned about duplication, create a “selector” function and place it alongside your reducers:
// Reducer is the default export
export default combineReducers({
planets,
people
});
// Selectors are named exports
export function isAllLoaded(state) {
return state.people.loaded && state.planets.loaded;
}
Now you can import selectors from your components and use them in mapStateToProps
function inside any component:
import { isAllLoaded } from '../reducers';
function mapStateToProps(state) {
return {
loaded: isAllLoaded(state),
people: state.people.items,
planets: state.planet.items
};
}
- Does Dan by calling other reducers mean calling nested reducers?
Yes, reducer composition usually means calling nested reducers.
Please refer to my free Egghead tutorial videos on this topic:
combineReducers()
combineReducers()
from ScratchA loaded flag in your store state makes perfect sense.
However you've got too many of them. You've got state.people.loaded
, state.planets.loaded
as well as state.loaded
. The latter is derived from the first two. Your store really shouldn't contain derived state. Either have just the first two or just the latter.
My recommendation would be to keep the first two, i.e. state.people.loaded
and state.planets.loaded
. Then your connected component can derive an ultimate loaded state. e.g.
function mapStateToProps(state) {
return {
loaded: state.people.loaded && state.planets.loaded,
people: state.people.items,
planets: state.planet.items
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With