higher-order react component that manages data fetching with redux
A central place to decide what data needs to be fetched for a component and what not.
Does not render the main component before everything is loaded.
This is WIP and needs tests and more detailed examples. Already seems to do fine in production, though 😁
requires react >= 0.14. You also need to install react-redux@4.x
if you haven’t already.
$ npm i -S redux-withdata
Just use redux like you would usually do. Define conditions for dispatching actions in the withData
HOC’s mapStateToRequests
callback. Use compose
from redux together with connect
from react-redux.
Make sure that connect
is the last argument of compose
. The connect()
ed component is rendered as soon as all data has loaded. You can safely assume that your mapStateToProps
function works on a populated store state. Optionally, you may pass a LoadingComponent
that is displayed in the meanwhile.
import { compose } from "redux"
import { connect } from "react-redux"
import withData from "redux-withdata"
class TodoApp extends React.Component {
// …
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch)
}
}
function mapStateToRequests(stateProps, dispatchProps, ownProps) {
// store state -> action creator callbacks
// returned keys are arbitrary and passed to LoadingComponent as an array (example will follow)
// only values that are callbacks will be executed
// if no callbacks are left we assume that loading has finished and render the original component
// roll your own conditions below
// in this example, the reducer should set `loaded` to true upon successful fetching
return {
// do not execute the action creator here, just pass it as a callback
// this expression is false if `loaded` is false.
todos: !stateProps.todos.loaded && dispatchProps.actions.requestTodos
}
}
function mapStateToProps(state) {
// our "original" component is the `connect()`ed component
// it won't be rendered and this won't get called until the data has loaded and all conditions have met
// state.todos.byId has already been populated and we can do some transforms
return {
todos: _.mapValues(state.todos.byId, v => /* ... */)
}
}
// no need to `mapDispatchToProps` twice, connect(TodoApp) is passed these props
compose(withData(mapDispatchToProps, mapStateToRequests), connect(mapStateToProps))(TodoApp)
Internally, this is passed directly to connect
and works just the same. Check out the react-redux api for more details.
This function is similar to mergeProps
. You may use data from the store (stateProps
), action props that you defined in mapDispatchToProps
(dispatchProps
) and props passed directly to the HOC (ownProps
). This is the place to define conditions for actions to be executed in order to satisfy those same conditions after the store has been updated accordingly.
Return an object with arbitrary keys and values. All values that are callbacks are assumed to be action creators that are already bound to dispatch
, other values are ignored. If that object does not contain any callbacks anymore, the original component is rendered and loading is finished.
If optionally passed, this component will be used to create a „loading“ view. All currently loading keys are passed to the component in an array through a loading
prop. You may use this prop to display a detailed loading status. This component is unmounted as soon as the loading
array is empty.