Skip to content

Instantly share code, notes, and snippets.

@deoqc
Last active May 16, 2016 15:34
Show Gist options
  • Save deoqc/6a5b3b45d609b3d76f937b509b495222 to your computer and use it in GitHub Desktop.
Save deoqc/6a5b3b45d609b3d76f937b509b495222 to your computer and use it in GitHub Desktop.
Pagination with Apollo-Client
// Pagination
export const PAGINATION_INIT_FETCH_REQUEST = "pagination/INIT_FETCH_REQUEST";
export const PAGINATION_ADD_FETCH_REQUEST = "pagination/ADD_FETCH_REQUEST";
export const PAGINATION_INIT_FETCH_SUCCESS = "pagination/INIT_FETCH_SUCCESS";
export const PAGINATION_ADD_FETCH_SUCCESS = "pagination/ADD_FETCH_SUCCESS";
export const PAGINATION_INIT_FETCH_FAIL = "pagination/INIT_FETCH_FAIL";
export const PAGINATION_ADD_FETCH_FAIL = "pagination/ADD_FETCH_FAIL";
export const PAGINATION_IS_FETCHING_FAIL = "pagination/IS_FETCHING_FAIL";
export const PAGINATION_END_REACHED = "pagination/END_REACHED";
export const PAGINATION_CLEAR = "pagination/CLEAR";
import * as types from '../types';
import { client } from '../../apollo';
import gql from 'apollo-client/gql';
const paginationInitFetchRequest = (name, query, variables, path) => ({
type: types.PAGINATION_INIT_FETCH_REQUEST,
payload: { name, config: {query, variables, path } },
});
const paginationAddFetchRequest = (name) => ({
type: types.PAGINATION_ADD_FETCH_REQUEST,
payload: { name },
});
const paginationInitFetchSuccess = (name, data) => ({
type: types.PAGINATION_INIT_FETCH_SUCCESS,
payload: { name, data },
});
const paginationAddFetchSuccess = (name, data) => ({
type: types.PAGINATION_ADD_FETCH_SUCCESS,
payload: { name, data },
});
const paginationEndReached = (name) => ({
type: types.PAGINATION_END_REACHED,
payload: { name },
});
const paginationIsFetchingFail = (name) => ({
type: types.PAGINATION_IS_FETCHING_FAIL,
payload: { name },
});
const paginationInitFetchFail = (name, error) => ({
type: types.PAGINATION_INIT_FETCH_FAIL,
payload: { name, error },
});
const paginationAddFetchFail = (name, error) => ({
type: types.PAGINATION_ADD_FETCH_FAIL,
payload: { name, error },
});
// Async Action Creator
export const paginationInitFetch = (name, query, variables, path, first) => (dispatch) => {
dispatch(paginationInitFetchRequest(name, query, variables, path));
client.query({
query: gql`${query}`,
variables: {
first,
...variables,
},
forceFetch: false,
returnPartialData: false,
}).then(({data, errors}) => {
if (data && !errors) {
dispatch(paginationInitFetchSuccess(name, data));
}
if (errors) {
dispatch(paginationInitFetchFail(name, errors));
}
});
};
export const paginationAddFetch = (name, add) => (dispatch, getState) => {
const state = getState();
// check if isFetching
if (state.pagination.getIn([name, 'isFetching'])) {
return dispatch(paginationIsFetchingFail(name));
}
const path = state.pagination.getIn([name, 'config', 'path']);
// check if hasNextPage
if (!state.pagination.getIn([name, ...path, 'pageInfo', 'hasNextPage'])) {
return dispatch(paginationEndReached(name));
}
// everything checks: dispatch action
dispatch(paginationAddFetchRequest(name));
const data = state.pagination.get(name),
query = data.getIn(['config', 'query']),
variables = data.getIn(['config', 'variables']).toJS(),
isFetching = data.get('isFetching'),
hasNextPage = data.getIn([...path, 'pageInfo', 'hasNextPage']),
cursor = data.getIn([...path, 'pageInfo', 'endCursor']);
client.query({
query: gql`${query}`,
variables: {
...variables,
first: add,
after: cursor,
},
forceFetch: false,
returnPartialData: false,
}).then(({data, errors}) => {
if (data && !errors) {
dispatch(paginationAddFetchSuccess(name, data));
}
if (errors) {
dispatch(paginationAddFetchFail(name, errors));
}
});
};
export const paginationClear = (name) => ({
type: types.PAGINATION_CLEAR,
payload: { name },
});
import { fromJS, OrderedMap } from 'immutable';
import * as types from '../../actions/types';
const INITIAL_STATE = fromJS({})
export default function(state = INITIAL_STATE, action) {
const { type, payload } = action; // don't use meta
switch (type) {
case types.PAGINATION_INIT_FETCH_REQUEST:
return state.set(payload.name, fromJS({
loading: true,
isFetching: true,
config: payload.config,
}));
case types.PAGINATION_ADD_FETCH_REQUEST:
return state.update(payload.name, (state) => (
state.mergeDeep({
isFetching: true,
})
));
case types.PAGINATION_INIT_FETCH_FAIL:
return state.update(payload.name, (state) => (
state.mergeDeep({
isFetching: false,
error: payload.error,
}).delete('loading')
));
case types.PAGINATION_ADD_FETCH_FAIL:
return state.update(payload.name, (state) => (
state.mergeDeep({
isFetching: false,
error: payload.error,
})
));
case types.PAGINATION_IS_FETCHING_FAIL:
return state;
case types.PAGINATION_INIT_FETCH_SUCCESS:
return state.update(payload.name, (state) => (
state.mergeDeep({
isFetching: false,
...payload.data,
}).delete('error').delete('loading')
));
case types.PAGINATION_ADD_FETCH_SUCCESS:
return state.withMutations(state => {
const { name, data } = payload,
path = state.getIn([name, 'config', 'path']);
state.update(name, (state) => state.mergeDeep({
isFetching: false,
}).delete('error'));
state.updateIn(
[name, ...path, 'edges'],
(edges) => edges.concat(
fromJS(data).getIn([...path, 'edges'])
)
);
state.setIn(
[name, ...path, 'pageInfo'],
fromJS(data).getIn([...path, 'pageInfo'])
);
});
case types.PAGINATION_END_REACHED:
return state;
case types.PAGINATION_CLEAR:
return state.delete(payload.name);
}
// default
return state;
}
import React, {
createClass
} from 'react-native';
import { connect } from 'react-redux';
import {
paginationInitFetch,
paginationAddFetch,
paginationClear
} from '.../flux/actions/Pagination';
const Container = (Component) => createClass({
componentWillMount() {
this.props.initFetch();
},
componentWillUnmount() {
this.props.clear();
},
render() {
const { initFetch, clear, ...props} = this.props;
return (
<Component {...props} />
);
},
});
export default (config) => (Component) => {
const mapStateToProps = (state, ownProps) => ({
data: state.pagination.get(config.name) ? state.pagination.get(config.name).toJS() : {loading: true},
variables: config.variables(state, ownProps),
});
const mapDispatchToProps = (dispatch) => ({
initFetch: (variables) => () => dispatch(paginationInitFetch(
config.name,
config.query,
variables, // initial variables must/can not change
config.path,
config.initCount,
)),
addFetch: () => dispatch(paginationAddFetch(
config.name,
config.addCount,
)),
clear: () => dispatch(paginationClear(config.name)),
});
const mergeProps = (stateProps, dispatchProps, ownProps) => ({
data: stateProps.data,
initFetch: dispatchProps.initFetch(stateProps.variables),
addFetch: dispatchProps.addFetch,
clear: dispatchProps.clear,
...ownProps,
});
return connect(
mapStateToProps,
mapDispatchToProps,
mergeProps
)(Container(Component));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment