facebook / relay

Relay is a JavaScript framework for building data-driven React applications.
https://relay.dev
MIT License
18.42k stars 1.83k forks source link

createRefetchContainer does not update props #4181

Open Dregorio1 opened 1 year ago

Dregorio1 commented 1 year ago

I have code like:

import { graphql } from 'react-relay';
import Foo from '../Foo';
import enviroment from '../enviroment';

const schemaQuery = graphql`query FooBetterQuery($filter: Filter, $pageInfo: PageInfo) {
  foo(filter: $filter, pageInfo: $pageInfo) {
    ...Foo_foo @arguments(filter: $filter, pageInfo: $pageInfo)
  }
}`;

export default class FooBetter extends Component {
  renderQuery = ({ props, error }) => (
    <Foo
      {...props}
      error={error}
    />
  );

  getVariables = () => ({
    pageInfo: {
      offset: 0,
      count: 10,
    },
  })

  render() {
    return (
        <QueryRenderer
            environment={enviroment}
            query={schemaQuery}
            variables={this.getVariables()}
            render={this.renderQuery}
      />
    );
  }
}

and for Foo:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { DataGrid } from '@mui/x-data-grid';
import { createRefetchContainer, graphql } from 'react-relay';

export class Foo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: true,
      data: [],
      total: 0,
      page: 1,
      pageSize: 10,
    };
  }

  componentDidUpdate(prevProps, prevState) {
    const { foo } = this.props;
    if (prevState.data !== foo.items
    ) {
      this.setState((old) => ({
        pageState: {
          ...old.pageState, isLoading: false, data: foo.items, total: 200,
        },
      }));
    }
  }

  _loadMore(page) {
    // Increments the number of stories being rendered by 10.
    const { relay } = this.props;
    const refetchVariables = () => ({
      pageInfo: {
        offset: (page - 1) * 10,
        count: 10,
      },
    });
    this.setState((old) => ({ pageState: { ...old.pageState, isLoading: true } }));
    relay.refetch(refetchVariables, null, null, { force: true });
  }

  render() {
    const { data } = this.state;
    return (
        <DataGrid
          // ...
          onPageChange={(newPage) => {
            this._loadMore(newPage + 1);
            this.setState((old) => ({ pageState: { ...old.pageState, page: newPage + 1 } }));
          }}
        />
    );
  }
}

Foo.propTypes = {
  foo: PropTypes.object,
  relay: PropTypes.object,
};

Foo.defaultProps = {
  foo: {},
  relay: {},
};

const schemaQuery = graphql`query FooQuery($filter: Filter, $pageInfo: PageInfo) {
  foo(filter: $filter, pageInfo: $pageInfo) {
    ...Foo_foo @arguments(filter: $filter, pageInfo: $pageInfo)
  }
}`;

export default createRefetchContainer(Foo,
  {
    foo: graphql`fragment Foo_foo on FooList 
    @argumentDefinitions(
      filter: {type: "Filter"}
      pageInfo: {type: "PageInfo"}
    ) {
      items {
        name
      },
      page {
        offset
        nextOffset
        hasNextPage
      }
    }`,
  },
  schemaQuery);

When in DataGrid I click on the next page in DevTools I see correct request and response, but data in props does not change. What is the reason?

michael-haberzettel commented 1 year ago

Hello, I have exactly the same issue. Reponse from the server is provided with data but the render property of QueryRenderer is not re-fired correctly.

<QueryRenderer
        fetchPolicy="store-or-network'"
        query={graphql`
            ...
        `}
        render={({ error, props }: { error: ?Error, props: ?TopBarNavRespondBadgeQuery$data }) => {
            console.log('RENDERING', error, props)
            return error || !props ? <>Loading</> : <>Success</>
        }}
    />

My console output displays : RENDERING, null, null one time indefinitely.