Свойства Relay Modern RefetchContainer не передаются компоненту

У меня возникли проблемы с настройкой refetchContainer в Relay Modern. Родительский компонент - это QueryRenderer, который запускает начальный запрос, соответствующим образом заполняя свойства дочернего компонента (a-pro-riately? Eh? Eh ?!). RefetchContainer определяет все наши переменные, а в событии onChange поля ввода повторно запускает запрос с новыми переменными. Все это работает отлично, за исключением того, что реквизиты ребенка никогда не обновляются новыми полученными данными. Я могу развернуть хранилище Relay и увидеть, что запрос действительно был получен с соответствующими данными. Некоторое время я бился об это головой, и я был бы признателен за некоторую помощь. Наверное, мне не хватает чего-то простого. И Господь знает, что документации Relay Modern очень мало.

Я ковырялся и не нашел подходящего решения. Похоже, у этого парня похожая проблема: повторная выборка реле не показывает результат

Родительский компонент с QueryRenderer:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { graphql, QueryRenderer } from 'react-relay';
import Search from './Search';

const propTypes = {
  auth: PropTypes.object.isRequired,
};

class SearchContainer extends Component {
  render() {
    return (
      <QueryRenderer
        query={graphql`
          query SearchContainerQuery($search: String!){
            users: searchUsers(search:$search, first:10){
              ...Search_users
            }
          }`}
        variables={{ search: 'someDefaultSearch' }}
        environment={this.props.auth.environment}
        render={({ error, props }) => {
          if (error) {
            console.log(error);
          }
          if (props) {
            return <Search users={props.users} />;
          }
          return <div>No Dice</div>;
        }}
      />
    );
  }
}

SearchContainer.propTypes = propTypes;

export default connect(state => ({ auth: state.auth }))(SearchContainer);

Дочерний компонент с createRefetchContainer:

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { createRefetchContainer, graphql } from 'react-relay';

const propTypes = {
  relay: PropTypes.object.isRequired,
  users: PropTypes.object,
};

const defaultProps = {
  users: {},
};

class Search extends Component {
  render() {
    return (
      <div>
        <input
          type="text"
          onChange={(e) => {
            e.preventDefault();
            this.props.relay.refetch({
              search: e.target.value,
            });
          }}
        />
        <ul>
          {this.props.users.nodes.map(user =>
            <li key={user.id}>{user.username}</li>,
          )}
        </ul>
      </div>
    );
  }
}

Search.propTypes = propTypes;
Search.defaultProps = defaultProps;

export default createRefetchContainer(
  Search,
  {
    users: graphql.experimental`
      fragment Search_users on SearchUsersConnection
      @argumentDefinitions(
        search: {type: "String", defaultValue: ""}
      ) {
        nodes {
            id
            displayName
            username
          }
      }
    `,
  },
  graphql.experimental`
    query SearchRefetchQuery($search: String!) {
      users: searchUsers(search:$search, first:10){
        ...Search_users @arguments(search: $search)
      }
    }
  `,
);

GraphQL выглядит так:

# A connection to a list of `User` values.
type SearchUsersConnection {
  # Information to aid in pagination.
  pageInfo: PageInfo!

  # The count of *all* `User` you could get from the connection.
  totalCount: Int

  # A list of edges which contains the `User` and cursor to aid in pagination.
  edges: [SearchUsersEdge]

  # A list of `User` objects.
  nodes: [User]
}

Сетевые вызовы выполняются надлежащим образом, и данные возвращаются должным образом. NetworkCalls

Похоже, что директиву @arguments можно исключить из запроса refetch здесь:

query SearchRefetchQuery($search: String!) {
      users: searchUsers(search:$search, first:10){
          ...Search_users @arguments(search: $search)
      }
}

(удаление, похоже, не имеет никакого эффекта)

Я пробовал добавить директиву @arguments к фрагменту родительского компонента в соответствии с приведенной здесь рекомендацией: Передавать переменные в контейнер фрагментов в relay modern, но безрезультатно.


person cntrlz    schedule 21.07.2017    source источник
comment
извините, не очень помогло .. fwiw Я полностью отказался от refetchContainers для простой передачи новых свойств QueryRenderer, который выполняет свою работу. пример: github.com/NCI-GDC/portal-ui/blob/next/src/packages/@ncigdc/   -  person azium    schedule 21.07.2017
comment
@azium Ага, похоже, все работает нормально. Меня просто беспокоит, что refetchContainer, специально разработанный для этого, так сложно настроить!   -  person cntrlz    schedule 22.07.2017
comment
У меня такая же проблема, и я СЛИШКОМ бьюсь головой об стену :(   -  person Mani Shooshtari    schedule 12.11.2017
comment
У меня почти идентичный запрос работает нормально, и я не могу найти никаких явных ошибок в вашем коде. Вы пробовали обновиться до более свежей версии? ... или, может быть, попробуйте развернуть его из redux, чтобы посмотреть, работает ли он сам по себе ...   -  person hisa_py    schedule 15.11.2017
comment
Та же проблема, и я пробовал всевозможные варианты. @hisa_py Я использую самую последнюю версию и пробовал это.   -  person Martin Dawson    schedule 20.12.2017
comment
О, я думаю, что я тоже столкнулся с подобной проблемой. Если я правильно помню, я считаю, что id Relay, который использует для идентификации компонента, включает переменные для начального запроса, поэтому попробуйте развернуть фрагмент контейнера в QueryRenderer, переместив searchUsers (..) во фрагмент, а затем просто в запрос. ..Search_user. Вам нужно будет определить Search_user как фрагмент вашего корня, то есть: в моем случае это будет что-то вроде fragment Search_user on RootQueryType. Мое последнее предложение - включить отладчик и посмотреть, что происходит, когда вы получите данные.   -  person hisa_py    schedule 21.12.2017
comment
@hisa_py Ух ты, перемещение запроса во фрагмент сработало. Это должно быть ошибкой, в противном случае пример, опубликованный на facebook, не должен работать. Отправьте ответ, и я зачислю вам награду. Огромное спасибо. Вот проблема, которую я поднял: github.com/facebook/relay/issues/2244   -  person Martin Dawson    schedule 22.12.2017


Ответы (2)


Как упоминалось в других ответах, это действительно ожидаемое поведение, и я постараюсь немного расширить ответ.

Когда вызывается refetch и выполняется refetchQuery, Relay фактически не использует результат запроса для повторной визуализации компонента. Все, что он делает, - это нормализует полезную нагрузку в магазине и активирует все соответствующие подписки. Это означает, что если полученные данные не связаны с данными, на которые подписан смонтированный контейнер (например, с использованием совершенно другого идентификатора узла, который не имеет перекрытия данных), то компонент не будет повторно визуализироваться.

В этом конкретном сценарии причина, по которой контейнер не выполняет повторную визуализацию, т.е. почему он не подписан на изменения, инициированные refetchQuery, связана с тем, как настроены подписки. Подписка - это в основном подписка на snapshot, который в основном представляет собой конкретный фрагмент, а также данные и записи, связанные с ним в заданный момент времени - когда записи, связанные с моментальным снимком, изменяются, подписка для этого моментального снимка будет уведомлена об этом. изменения.

В этом случае, учитывая, что фрагмент для контейнера не имеет переменных, моментальный снимок, на который он будет подписываться, будет связан только с начальным узлом; после выполнения повторной выборки и обновления хранилища записи для нового узла будут обновлены, но запись, связанная с этим исходным моментальным снимком, не изменилась, поэтому контейнер, на который он подписан, не будет уведомлен.

Это означает, что контейнеры Refetch действительно предназначены для использования только при изменении переменных во фрагменте компонента. Приведенные здесь решения действительны, т. Е. Включение переменных во фрагмент для контейнера повторной выборки или установка новых переменных на один уровень выше в QueryRenderer.

Это определенно можно прояснить в документации, поэтому я обновлю их, чтобы отразить это. Надеюсь, это поможет!

person jstejada    schedule 03.01.2018

О, я думаю, что столкнулся с похожей проблемой. Если я правильно помню, я считаю, что реле «id», используемое для идентификации компонента, которому передаются реквизиты, включает переменные для начального запроса, поэтому попробуйте развернуть фрагмент контейнера из запроса QueryRenderer.

У меня сработала примерно следующая установка:

<QueryRenderer
  variables={{search: 'someDefaultSearch', count: 10}}
  query={graphql`
    query SearchContainerQuery($search: String!, $count: Int!){
      # if we want defaults we need to "prepare" them from here
      ...Search_users @arguments(search: $search, count: $count)
    }
  `}
  environment={this.props.auth.environment}
  render={({ error, props: relayProps }) => {
    // I use relayProps to avoid possible naming conflicts
    if (error) {
      console.log(error);
    }
    if (relayProps) {
      // the users props is "masked" from the root that's why we pass it as is
      return <Search users={relayProps} />;
    }
    return <div>No Dice</div>;
  }}
/>

И refetchContainer будет выглядеть примерно так:

export default createRefetchContainer(
  Search,
  {
    users: graphql`
      # In my schema the root is called RootQueryType 
      fragment Search_users on RootQueryType
      @argumentDefinitions(
        search: {type: "String"}
        count: {type: "Int!"}
      ) {
        searchUsers(search: $search, first: $count) {
          nodes {
            id
            displayName
            username
          }
        }
      }
    `
  },
  graphql`
    query SearchRefetchQuery($search: String!, $count: Int!) {
      ...Search_users @arguments(search: $search, count: $count)
    }
  `
);

Обратите внимание, что в приведенном выше примере предполагается Relay v1.3, где graphql.experimental устарел. Кроме того, я не помню, можно ли с помощью трюка @arguments заставить работать ваш подход по сглаживанию searchUsers.

Мое последнее предложение - включить отладчик и посмотреть, что происходит, когда вы получите данные.

Наконец, согласно вашим комментариям, я согласен, что это может быть ОШИБКА. Посмотрим, как будут развиваться дела по проблеме, о которой вы сообщили.

person hisa_py    schedule 22.12.2017