LordDave LordDave - 2 months ago 64
Javascript Question

RelayJS infinite scroll

I'm attempting to make a infinite scroll pagination in my React-Relay frontend, but without a success.

At this moment, I have this React component ...

class List extends Component {
state = { loading: false };

componentDidMount() {
window.onscroll = () => {
if (!this.state.loading
&& (window.innerHeight + window.scrollY)
>= document.body.offsetHeight) {

this.setState({loading: true}, () => {
this.props.relay.setVariables({
count: this.props.relay.variables.count + 5
}, (readyState) => {
if (readyState.done) {
this.setState({ loading: false });
}
});
});
}
}
}

render() {
return (
<div>
{this.props.viewer.products.edges.map(function(product, i) {
return (<Item key={i} product={product.node} />);
})}
</div>
);
}
};


... wrapped in Relay container

export default Relay.createContainer(List, {
initialVariables: {
count: 5
},
fragments: {
viewer: () => Relay.QL`
fragment on Viewer {
products(first: $count) {
total
edges {
node {
${Item.getFragment('product')}
}
}
}
}
`,
}
});


The logic behind this seems quite easy. If you reach a certain
scrollY
position, set to
count
variable a new inceremented value, which extends your list of Edges.

But this concept will lead to a situation, where in the beginning I query the database for first 5 records, but lately while I keep scrolling a will query for N+5 records. In the end I will query the database for a whole table (thousands of records)! Which is quite unacceptable.

So I'm trying to implement a
cursors
, but I don't know how to just fetch data from a connection and extend the result. This approach returns me a "paginated" list.




Also, the above code gives me this error, when I scroll down


resolveImmediate.js?39d8:27 Uncaught Invariant Violation: performUpdateIfNecessary: Unexpected batch number (current 19, pending 18)


I will be very thankful for any help or example!

Answer

Problem solved!

About my concern of fetching N+5 records over and over again with each scroll, it seems that Relay is quite intelligent, because it automatically "paginate" my connection by querying cursor and managing the after arg in my connection.

And with little help from graphql-sequelize on my backend, it simply turns into this query

SELECT ... FROM product ORDER BY product.id ASC LIMIT 5 OFFSET 10

Which actually solves my first problem about the performance. :)

So my Relay.QL query has underhood transpiled into this

query ListPage_ViewerRelayQL($id_0:ID!) {
  node(id:$id_0) {
    ...F1
  }
}
fragment F0 on Product {
  id,
  title,
  price
}
fragment F1 on Viewer {
  _products4tSFq4:products(after:"YXJyYXljb25uZWN0aW9uJDUkNA==",first:5) {
    edges {
      cursor,
      node {
        id,
        ...F0
      }
    },
    pageInfo {
      hasNextPage,
      hasPreviousPage
    }
  },
  id
}

and when I ran this query on my GraphQL backend, it returned me empty result

{
  "data": {
    "node": null
  }
}

Then I immediately realized where is the problem and why my Relay crashes down.

The problem is with refetching the viewer. I did not properly implemented nodeDefinitions and when the viewer ID hit idFetcher and typeResolver, it failed and returned a null. After that on the client side, Relay was unable to finish the refetching of my connection and crashed down!

After small repairment and fixes on the Backend-side, my infinite scroll works like a charm! :)