react-simple-async v0.4.0
react-simple-async
Universal library for fetching react dependencies
Installation
npm i -S react-simple-async
How does it work?
On server, we use renderProps
from react-router match callback. We iterate
over components, looking after components decorated with asyncData
On client we use AsyncHandler
component in react-router render, where we
bind to componentWillMount to check data after startup and then componentWillReceiveProps
to check for route changing
We don't do any optimisations, as we can do that on connected component, which receive in decorated function all it needs, like location, params, dispatch, getState
#Example
On client
import React from 'react';
import ReactDOM from 'react-dom';
import createHistory from 'history/lib/createBrowserHistory';
import {createStore} from 'redux';
import reducers from './reducers'; //custom reducers
import {getRoutes} from './routes'; //custom routes
import {Provider} from 'react-redux';
import {Router, browserHistory } from 'react-router';
import {AsyncHandler} from 'react-simple-async';
const store = createStore();
const routes = getRoutes(store);
const mountPoint = document.getElementById('content');
const custom = {
getState:store.getState,
dispatch:store.dispatch
}
ReactDOM.render(
(
<Router
history={browserHistory}
routes={routes}
render={(props) => <AsyncHandler custom={custom} {...props}/>}
/>
), mountPoint
);
On server
... //import createStore, getRoutes, reducers etc.
import {match} from 'react-router';
import createHistory from 'history/lib/createMemoryHistory';
import {getDataDependencies} from 'react-simple-async';
match({history,routes,location:req.originalUrl},(error, redirectLocation, renderProps) => {
if (redirectLocation) {
res.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (error) {
console.error('ROUTER ERROR:', error);
res.status(500);
hydrateOnClient(); // error in router, you should try to hydrate app on client
} else if(renderProps) {
//here starts our work, we get all connected promises and after they are resolved, we render app for client
Promise.all(getDataDependencies(renderProps.components)(store,renderProps.location, renderProps.params))
.then(
... //custom render method
)
.catch((e) => {
console.error(e);
hydrateOnClient();
});
} else {
console.error(err);
res.status(404); // page not found in router, you should try to hydrate app on client
hydrateOnClient();
}
})
Component decorator
For decorating component you can use default export. Decorator must contain function, which return promise.
If you have more than one promises, wrap them in Promise.all
import asyncData from 'react-simple-async';
@asyncData(function({getState, dispatch}){ //available params, location and all keys defined in custom prop of AsyncHandler
let promises = [];
if(isSomethingFetched(getState())){ //check if you really need fetch
promises.push(dispatch(fetchSomething())); //dispatch action
}
return Promise.all(promises);
})
export default class App extends Component {
...
Don't want to use decorators?
You can use same as above, only don't export class, but result of asyncData
export default asyncData(function(){
//fetch dependencies as above
})(App); //exported component
Future?
- add full working example
- write tests