I\'m trying to work out the cleanest way to load the initial state of my Redux stores when it comes from API calls.
I understand that the typical way of providing the in
I also encountered the same problem (also building an electron app). A part of my store has application settings which gets persisted on local file system and I needed to load it asynchronously on application's startup.
This is what I come up with. Being a "newbie" with React/Redux, I am very much interested in knowing the thoughts of the community on my approach and how it can be improved.
I created a method which loads the store asynchronously. This method returns a Promise
which contains the store
object.
export const configureStoreAsync = () => {
return new Promise((resolve) => {
const initialState = initialStoreState;//default initial store state
try {
//do some async stuff here to manipulate initial state...like read from local disk etc.
//This is again wrapped in its own Promises.
const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
resolve(store);
});
} catch (error) {
//To do .... log error!
const store = createStore(rootReducer, initialState, applyMiddleware(thunk));
console.log(store.getState());
resolve(store);
}
});
};
Then in my application entry point, here's how I used it:
configureStoreAsync().then(result => {
const store = result;
return ReactDOM.render(
<Provider store={store}>
<App store={store}/>
</Provider>,
document.getElementById('Main'));
});
Like I said, this is my naive attempt at solving this problem and I am sure there must be better ways of handling this problem. I would be very much interested in knowing how this can be improved.
My app startup workflow:
I achieved that by:
root.js
const store = createStore(
rootReducer,
applyMiddleware(
...,
actionCallbackOnceMiddleware(INITIAL_AJAX_END, render)
)
)
function render() {
ReactDOM.render(
<Provider store={store}>
<RootComponent/>
</Provider>,
document.getElementById('root')
)
document.getElementById('loading').dispatchEvent(new Event('hide'))
}
store.dispatch(initialAjaxAction());
middleware/actionCallbackOnce.js
export default (actionType, callback) => store => next => {
let called = false;
return action => {
next(action);
if (!called && action.type === actionType) {
called = true;
callback();
}
}
}
index.html
<div id="loading">
<span>Loading</span>
<style type="text/css">...</style>
<script>
(function(loading){
loading.addEventListener('hide', function(){
loading.remove();
});
loading.addEventListener('error', function(){
loading.querySelector('span').textContent = "Error";
});
})(document.getElementById('loading'));
</script>
</div>
<div id="root"></div>
As far as I can tell, you have only two options (logically):
Option 1 must be done using an action:
The only way to change the state is to emit an action, an object describing what happened.
— One of "Three Principles" in the docs
This is what you've tried, but you think it is crude for some reason.
The alternative is just to call createStore
after your asynch request has resolved. One solution has already been posted (by @Gaurav Mantri) using a Promise
object, which is a nice approach.
I would recommend against this, since you will likely have multiple modules trying to require
or import
your store (or store.dispatch
, or store.subscribe
) before it exists; they would all have to be made to expect Promise
s. The first method is the most Redux-y.