Search code examples
reactjsrelayjsgraphql

How to render a long list in react-relay?


A lazy scrolling list component for react-web backing onto a Relay Connection, with paginated fetching, while offering good performance and memory characteristics.

Two things to manage

  • Data fetching through providing pagination parameters to query, manipulation of the relay store
  • Component rendering, manipulation of the virtual DOM

Is there some particular list component which handles this well? Is there an established pattern for implementing this common mechanism?


Solution

  • This pattern is pretty much the representative scenario for connections. Here's a hypothetical <PostsIndex> component that shows a list of posts with a "load more" button. If you don't want to explicitly change the UI when in the isLoading state you could delete the constructor and the setVariables callback. Adding viewport based infinite scrolling would not be hard either; you'd just need to wire a scroll listener up to you setVariables call.

    class PostsIndex extends React.Component {
      constructor(props) {
        super(props);
        this.state = {isLoading: false};
      }
    
      _handleLoadMore = () => {
        this.props.relay.setVariables({
          count: this.props.relay.variables.count + 10,
        }, ({ready, done, error, aborted}) => {
          this.setState({isLoading: !ready && !(done || error || aborted)});
        });
      }
    
      render() {
        return (
          <div>
            {
              this.props.viewer.posts.edges.map(({node}) => (
                <Post key={node.id} post={node} />
              ))
            }
            {
              this.props.viewer.posts.pageInfo.hasNextPage ?
                <LoadMoreButton
                  isLoading={this.state.isLoading}
                  onLoadMore={this._handleLoadMore}
                /> :
                null
            }
          </div>
        );
      }
    }
    
    export default Relay.createContainer(PostsIndex, {
      initialVariables: {
        count: 10,
      },
      fragments: {
        viewer: () => Relay.QL`
          fragment on User {
            posts(first: $count) {
              edges {
                node {
                  id
                  ${Post.getFragment('post')}
                }
              }
              pageInfo {
                hasNextPage
              }
            }
          }
        `,
      },
    });