relay-tools/react-router-relay

Paginating Next/Previous through a list: able to fetch prepared params before Relay QL Template call?

danmooney opened this issue · 4 comments

I'm having trouble paginating through a list of posts. I'm passing "before" and "after" as query strings with previous and next, respectively. When it's "before", I'd like to use the Relay pagination variables "before" and "last" with the startCursor from pageInfo. When it's "after", I'd like to use the Relay pagination variables "after" and first" with the endCursor from pageInfo. These variable combinations produce the desired normal pagination effect when I inspect in GraphiQL. Is there any way to modify the GraphQL query passed to Relay.QL based on the prepared params from the route? Any insight or feedback on a better implementation would be much appreciated.

My Router:

<Router
        history={browserHistory}
        render={applyRouterMiddleware(useRelay)}
        environment={Relay.Store}
    >
        <Route
            path="/"
            component={App}
            queries={StoreQueries}
        >
            <IndexRoute component={PostList} queries={StoreQueries} prepareParams={(params, {location}) => {
                let additionalParams = {};

                ['before', 'after'].forEach((locationQueryParam) => {
                    if (typeof location.query[locationQueryParam] !== 'undefined') {
                        additionalParams[locationQueryParam] = location.query[locationQueryParam];
                    }
                });

                return {
                    ...params,
                    ...additionalParams,

                }
            }} />
    </Route>
</Router>

My Relay Container:

PostList = Relay.createContainer(PostList, {
    initialVariables: {
        limit: 10,
        after: null,
        before: null
    },
    fragments: {
        store: () => {
            // can i fetch prepared params here to dynamically alter GraphQL query so I can change the arguments list for allPosts?
            return Relay.QL`
                fragment on Store {
                    id
                    allPosts(first: $limit, after: $after) {
                        pageInfo {
                            hasNextPage
                            hasPreviousPage
                            startCursor
                            endCursor
                        }
                        edges {
                            node {
                                id
                                ${Post.getFragment('post')}
                            }
                        }
                    }
                }
            `
        }
    }
});
taion commented

I think you just need to add both after: $after and before: $before to that query. The null one will just be ignored.

Apologies, I added before: $before as well as last: $last. I believe this is still an issue, however. How can one conditionally pass $limit to first if after is non-null, or pass $limit to last if before is non-null? Do I have to re-evaluate my location params in my fragment before constructing my template literal?

Relay.QL`
    fragment on Store {

        allPosts(first: $limit, last: $limit, before: $before, after: $after) {
            pageInfo {
                hasNextPage
                hasPreviousPage
                startCursor
                endCursor
            }
            edges {
                node {
                    id
                    ${Post.getFragment('post')}
                }
            }
        }
    }
`
taion commented

You need to set that up in prepareParams – make it either first or last depending on which pagination option is enabled. Or have e.g. a boolean flag for determining which one is relevant.

OK, I got it to work, albeit hackily. Used two separate limit variables in prepareParams, one for before and one for after.

<Router
        history={browserHistory}
        render={applyRouterMiddleware(useRelay)}
        environment={Relay.Store}
    >
        <Route
            path="/"
            component={App}
            queries={StoreQueries}
        >
            <IndexRoute component={PostList} queries={StoreQueries} prepareParams={(params, {location}) => {
                let additionalParams = {};

                ['before', 'after'].forEach((locationQueryParam) => {
                    if (typeof location.query[locationQueryParam] !== 'undefined') {
                        additionalParams[locationQueryParam] = location.query[locationQueryParam];
                    }
                });

               if (additionalParams.before) {
                    additionalParams.beforeLimit = 10;
                } else {
                    additionalParams.afterLimit = 10;
                }

                return {
                    ...params,
                    ...additionalParams,
                }
            }} />
    </Route>
</Router>

And my GraphQL signature is as such:
allPosts(first: $afterLimit, last: $beforeLimit, before: $before, after: $after)

Hope this helps someone.