Last active
May 24, 2016 13:47
-
-
Save MarkMurphy/9b964b3cfa7b30736734e6a908b3a6b4 to your computer and use it in GitHub Desktop.
redux-crud pagination
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// containers/AlbumPage.js | |
import React, { Component, PropTypes } from 'react' | |
import { connect } from 'react-redux' | |
import { browserHistory } from 'react-router' | |
import { fetchAlbum, deleteAlbum } from '../actions/albums' | |
import { listPhotos } from '../actions/photos' | |
import bindAll from 'lodash/bindAll' | |
import AlbumHeader from '../components/AlbumHeader' | |
import PhotoGallery from '../components/PhotoGallery' | |
function loadData(props) { | |
const { id } = props | |
props.fetchAlbum(id) | |
props.listPhotos({ album: id }) | |
} | |
class AlbumPage extends Component { | |
constructor(props) { | |
super(props) | |
bindAll(this, [ | |
'handleAlbumDelete' | |
]) | |
} | |
componentWillMount() { | |
loadData(this.props) | |
} | |
componentWillReceiveProps(nextProps) { | |
if (nextProps.id !== this.props.id) { | |
loadData(nextProps) | |
} | |
} | |
handleAlbumDelete(album) { | |
this.props.deleteAlbum(album.id) | |
browserHistory.push(`/albums`) | |
} | |
render() { | |
const { album, albumPhotos } = this.props | |
if (!album) { | |
return <div>Loading album...</div> | |
} | |
return ( | |
<div> | |
<AlbumHeader album={album} onAlbumDelete={this.handleAlbumDelete} /> | |
<PhotoGallery photos={albumPhotos} /> | |
</div> | |
) | |
} | |
} | |
function mapStateToProps(state, props) { | |
const { | |
albums, | |
photos, | |
pagination: { photosByAlbum }, | |
} = state | |
const id = props.params.id | |
const albumPhotosPagination = photosByAlbum[id] || { ids: [] } | |
const album = albums.find(album => album.id == id) | |
// Use this line (line 69) instead of the one above (line 67) if you modify redux-crud to use Maps instead of Arrays. | |
// const album = albums.get(id) | |
const albumPhotos = photos.filter(item => albumPhotosPagination.ids.includes(item.id)) | |
// Use this line (line 73) instead of the one above (line 71) if you modify redux-crud to use Maps instead of Arrays. | |
// const albumPhotos = albumPhotosPagination.ids.map(id => photos.get(id)) | |
return { | |
id, | |
album, | |
albumPhotos | |
} | |
} | |
const mapDispatchToProps = { | |
fetchAlbum, | |
deleteAlbum, | |
listPhotos | |
} | |
export default connect(mapStateToProps, mapDispatchToProps)(AlbumPage) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// reducers/pagination.js | |
import merge from 'lodash/merge' | |
import union from 'lodash/union' | |
import { combineReducers } from 'redux' | |
import { actionTypesFor } from 'redux-crud' | |
// Creates a reducer managing pagination, given the action types to handle, | |
// and a function telling how to extract the key from an action. | |
export function paginate({ types, mapActionToKey }) { | |
if (!Array.isArray(types) || types.length !== 3) { | |
throw new Error('Expected types to be an array of three elements.') | |
} | |
if (!types.every(t => typeof t === 'string')) { | |
throw new Error('Expected types to be strings.') | |
} | |
if (typeof mapActionToKey !== 'function') { | |
throw new Error('Expected mapActionToKey to be a function.') | |
} | |
const [ requestType, successType, failureType ] = types | |
const initialState = { | |
isFetching: false, | |
// nextPageUrl: undefined, | |
// pageCount: 0, | |
ids: [] | |
} | |
function updatePagination(state = initialState, action) { | |
switch (action.type) { | |
case requestType: | |
return merge({}, state, { | |
isFetching: true | |
}) | |
case successType: | |
return merge({}, state, { | |
isFetching: false, | |
ids: union(state.ids, action.records.map(record => record.id)), | |
// nextPageUrl: action.response.nextPageUrl, | |
// pageCount: state.pageCount + 1 | |
}) | |
case failureType: | |
return merge({}, state, { | |
isFetching: false | |
}) | |
default: | |
return state | |
} | |
} | |
return function updatePaginationByKey(state = {}, action) { | |
switch (action.type) { | |
case requestType: | |
case successType: | |
case failureType: | |
const key = mapActionToKey(action) | |
// If key is exactly `false`, skip this update. | |
if (typeof key === false) { | |
return state | |
} | |
if (typeof key !== 'string') { | |
throw new Error('Expected key to be a string.') | |
} | |
return merge({}, state, { | |
[key]: updatePagination(state[key], action) | |
}) | |
default: | |
return state | |
} | |
} | |
} | |
const albumActionTypes = actionTypesFor('albums') | |
const photoActionTypes = actionTypesFor('photos') | |
// Updates the pagination data for different actions. | |
export default pagination = combineReducers({ | |
albumsByUser: paginate({ | |
mapActionToKey: action => action.data.user || false, | |
types: [ | |
albumActionTypes.fetchStart, | |
albumActionTypes.fetchSuccess, | |
albumActionTypes.fetchError | |
] | |
}), | |
photosByAlbum: paginate({ | |
mapActionToKey: action => action.data.album || false, | |
types: [ | |
photoActionTypes.fetchStart, | |
photoActionTypes.fetchSuccess, | |
photoActionTypes.fetchError | |
] | |
}) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment