Как предварительно заполнить форму данными из запроса GraphQL с помощью Apollo 2?

У меня возникли трудности с получением данных из запроса Apollo 2 GraphQL в обычные поля формы.

Как к этому относятся другие? Ранее пробовал Relay и успешно устанавливал состояние полей формы с помощью конструктора компонентов.

Однако в Apollo, даже после часов поиска и чтения тонны документации, я все еще в неведении...

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

я совсем запуталась :/

Это то, что я сейчас придумал ... это работает. Но является ли это рекомендуемым способом выполнения этого требования?

import React from 'react';

import { Route, Redirect, Switch } from "react-router-dom";

import { graphql } from 'react-apollo';
import gql from 'graphql-tag';

// SEMANTIC-UI REACT
import { Container, Form, Button, Loader, Message } from "semantic-ui-react";


const MY_QUERY = gql`
  query {
    erp(id:"RXJwTm9kZToy") { 
      id
      name
      code
    }
  }`;


class Test extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
    }
  }

  componentWillReceiveProps(newProps) {
    if (newProps !== this.props) {
      this.setState({
        erp_name: (newProps.data.erp || "").name,
        erp_code: (newProps.data.erp || "").code
      });
    }
  }

  render() {
    if (this.props.data.loading) {
      return <Loader active inline='centered' />;
    } else if (this.props.data.error) {
      return <Message error content={this.props.data.error.message} />;
    } else {
      return (
        <Form>
          <ul>
            <li key={this.props.data.erp.id}>{this.props.data.erp.name}</li>
          </ul>
          <Form.Input name="erp_name" label="ERP Name" placeholder="Name" value={this.state.erp_name} />
          <Form.Input name="erp_code" label="ERP Code" placeholder="Code" value={this.state.erp_code} />
          <Button type='submit'>Submit</Button>
        </Form>
      );
    }
  }
}

export default graphql(MY_QUERY)(Test);

person isopterix    schedule 17.12.2017    source источник
comment
Не могли бы вы привести пример компонента вместе с запросом?   -  person Jake Lacey    schedule 17.12.2017


Ответы (2)


Это небольшой пример с React и ReduxForm. Может быть, это поможет вам.

В этом примере запрос будет выполнен при инициализации компонента и вернет данные, необходимые для заполнения формы.

Я надеюсь, что это поможет вам!

import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { compose } from 'recompose'
import { graphql, withApollo, gql } from 'react-apollo'
import { Form } from 'redux-form'

class MyExampleComponent extends Component {
  render() {
    const { userData } = this.props

    return (
      <Form
        initialValues={userData}
      />
    )
  }
}

MyExampleComponent.propTypes = {
  userData: PropTypes.object
}

const query = gql`
  query getUser($userId: ID) {
    user(id: $userId) {
      age
      firstName
      lastName
    }
  }
`

export default compose(
  withApollo,

  graphql(query, {
    props: ({ data }) => ({
      isLoading: data.loading,
      userData: data.user
    }),
    options: ({ params }) => ({
      variables: { id: params.id }
    })
  })
)(MyExampleComponent)
person Washington Braga    schedule 17.12.2017
comment
Все еще запутался... Это должно быть стандартной операцией. Тем не менее, нигде я не смотрел, похоже, у кого-то есть подобная проблема. - person isopterix; 17.12.2017

Подход, который я сейчас использую, заключается в том, чтобы отображать форму всегда, даже если данные еще недоступны. Таким образом, для загрузочной опоры не требуется проверка, поскольку предполагается, что все данные доступны. В случае, если вы проверяете поддержку загрузки и отображаете форму только после загрузки данных, клиентский браузер ждет с отображением формы до тех пор, пока данные не будут доступны, и это может раздражать, особенно когда для доступности данных требуется больше времени. Лучше пусть браузер использует это свободное время для создания формы уже в DOM.

Но как узнать начальное состояние из запроса graphql и предварительного заполнения?

Состояние используется для хранения входных данных формы, в то время как исходные данные из источника graphql НИКОГДА не загружаются в состояние! И каждый раз, когда нужны (правда) данные, они будут пересчитываться. Этот пересчет должен происходить только в одном месте, поэтому я ввел вспомогательную функцию getTruthData(). И везде, где вам нужны истинные данные, вы позволяете им обрабатывать эту вспомогательную функцию. Например. с рендерингом, а также с отправкой обновленных данных обратно в graphql mutate. И только в этой функции getTruthData() нужно будет вернуть данные по доступности. Не беспокойтесь о производительности, так как пересчет истинных данных происходит очень быстро, а кэширование приведет только к непредсказуемым результатам из-за природы повторного рендеринга React.

Я (пытался) обновить ваш код. Что я сделал:

  • удалите componentWillReceiveProps, так как он не понадобится
  • убрать галочку на загрузку в рендере
  • Представьте getTruthData()
  • переход от неконтролируемой к контролируемой форме (я заметил позже, хотя и не имел отношения к вопросу)

Код:

class Test extends React.Component {
  constructor(props) {
    super(props);
    this.getProfileTruth = this.getProfileTruth.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  getTruthData() {
    return {
      erp_id: this.props.data.erp.id || '',
      erp_name: this.state.erp_name || this.props.data.erp.name || '',
      erp_code: this.state.erp_code || this.props.data.erp.code || '',
    };

    // OR you might want to use the underscore helper for larger forms with more fields to keep like this:

    return _.defaults(
      {},
      this.state,
      this.props.data.erp,
      { erp_name: '', erp_code: '' },
    );
  }

  handleChange(data) {
    this.setState(data);
  }

  handleSubmit(e) {
    e.preventDefault();
    const { erp_id, erp_name, erp_code } = this.getProfileTruth();
    // run a mutate on graphql
  }


  render() {
    const { erp_id, erp_name, erp_code } = this.getProfileTruth();

    return (
      <Form>
        <ul>
          <li key={erp_id}>{erp.name}</li>
        </ul>
        <Form.Input
          name="erp_name"
          label="ERP Name"
          placeholder="Name"
          value={erp_name}
          onChange={e => this.handleChange({ erp_name: e.target.value })} />
        <Form.Input
          name="erp_code"
          label="ERP Code"
          placeholder="Code"
          value={erp_code}
          onChange={e => this.handleChange({ erp_code: e.target.value })} />
        <Button type='submit' onClick={this.handleSubmit}>Submit</Button>
      </Form>
    );
  }
}

Побочным эффектом может быть то, что если данные из graphql будут занимать очень много времени, форма уже может быть введена. С контролируемой формой вы можете проверить handleChange, чтобы разрешить изменения только при правильной загрузке данных. Вы даже можете деактивировать стиль кнопки.

person Bob Siefkes    schedule 07.08.2018