I was looking for a way to display a loading indicator in a PaginationContainer after calling loadMore() and I thought this.props.relay.isLoading() might do the trick. However, I see that the PaginationContainer's render() method is called only once after the network request (which I guess is expected), but isLoading() is always true. This seems like a bug since presumably the network request has completed prior to the refresh and therefore isLoading() should return false.
As an aside, this still leaves me with the question of how to display a loading indicator during paging in additional data via loadMore().
loadMore let's you pass in a callback. Are you able to use that for your use case?
the loadMore() callback is called after the render resulting from the completion of the loadMore request happens. So I don't see how I could use it in my use case.
As a workaround you can a flag in state of you component.
Something like
class TaskList extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
};
}
render() {
const { viewer, relay: { hasMore, isLoading, loadMore } } = this.props;
console.log('render');
return (
<div>
<List
fetching={this.state.loading}
isMoreItems={hasMore()}
loadItems={() => {
this.setState({ loading: true });
console.log('loadMore');
loadMore(3, () => {
console.log('loadMore finished');
this.setState({ loading: false });
});
}}
items={viewer.allTasks.edges}
row={({ node }) => <ListItem key={node.id}><TaskListItem task={node} /></ListItem>}
/>
{this.state.loading && <span>loading...</span>}
</div>
);
}
}
Can confirm. This is a reproducable bug. See https://github.com/DennisKo/relay-graphcool for a pagination example.
I'm having the same issue. Using the component state and passing loadMore a callback to update state as a workaround.
The issue here is that we don't trigger a re-render when the return value of isLoading would change. @josephsavona suggested we might create a client side field on the connection so we would only trigger a re-render when the status changes and the client is interested in the change of the status, but this seems a difficult change as it would require adding a client field extension to every *Connection type that has a @connection.
I propose, we do a migration to a boolean property instead by following these steps:
getIsLoading() function and a warning to the current isLoading() function to use the new function instead. The new getIsLoading() could probably be set as a different function when the value changes, so we trigger a re-render correctly.isLoading() function with a boolean and add a deprecation warning to getIsLoading to use the isLoading boolean instead.getIsLoading()The same should probably happen for hasMore.
Hey @dwwoelfel - this is related to areas you've been working in, do you mind taking a look?
@unp You can work around needing to have state by using forceUpdate(), something like:
handleClick = () => {
relay.loadMore(10, () => { this.forceUpdate() })
}
You're still required to use a class component but it avoids maintaining state anywhere. A solution that works with functional components would be great though.
Any updates since 9 months ago @leebyron or @dwwoelfel? I just ran into this too.
Probably it would be better to have the function mapProps(props) { return props; } (it's default implementation) in connectionConfig that does map props before propagating them to the component. If this function isn't specified in connectionConfig we will have the same behavior as is now (just because the props are not changed and there are no reasons to rerender component). But if this function is defined in connectionConfig it should be triggered each time when something was changed (like fragment property, isLoading() result, hasMore() result, external property that should be propagated to the component and so on).
This new method should solve the problem and simplify compatibility with previous version.
Still a thing. Not a big deal at all but it is mentioned in the docs. Probably the best is to remove isLoading method completely.
For those using React with function components, forceUpdate is no longer a thing. I made a hook to force an update as described in the React docs:
export const useForceRerender = () => {
const [_, forceRerender] = useReducer(x => x + 1, 0);
return forceRerender;
};
And then I use it in my component like this:
const forceRerender = useForceRerender();
const onLoadMore = () => {
loadMore(10, forceRerender);
forceRerender();
}
I found that I have to force a re-render both when calling loadMore and also when it finishes in order for my component to always be in sync with isLoading.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
Most helpful comment
Any updates since 9 months ago @leebyron or @dwwoelfel? I just ran into this too.