简体   繁体   English

如何使用Apollo 2使用GraphQL查询中的数据预填充表单?

[英]How to pre-fill a form with data from a GraphQL query using Apollo 2?

I am having difficulty getting the data from an Apollo 2 GraphQL query into regular form fields. 我很难将Apollo 2 GraphQL查询中的数据转换为常规表单字段。

How are others approaching this? 其他人如何接近这个? Previously tried Relay and was successful in setting the form fields state with the component constructor. 以前尝试过Relay并成功使用组件构造函数设置表单字段状态。

In Apollo however, even after searching for hours and reading tons of documentation I am still in the dark... 然而,在阿波罗,即使在搜索了几个小时并阅读了大量的文档之后,我仍然处于黑暗中......

During initialization of the component the state always returns null as the props are set at a later stage. 在组件初始化期间,状态始终返回null,因为在稍后阶段设置了props。 I tried to set it after loading, but that gets me into an endless loop... 我试着在加载后设置它,但这让我陷入了无尽的循环......

I am completely lost :/ 我完全迷失了:/

This is what I have now come up with... it works. 这就是我现在想出来的......它有效。 But is this the recommended way to approach this requirement? 但这是否是推荐的方法来满足这一要求?

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);

This is an small example with React and ReduxForm. 这是React和ReduxForm的一个小例子。 Maybe this will help you. 也许这会对你有所帮助。

In this example the query will be executed at the initialization of the component and will return the data needed to fill out the form. 在此示例中,查询将在组件初始化时执行,并将返回填写表单所需的数据。

I hope it helps you! 我希望它对你有所帮助!

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)

The approach I'm currently using is to render the form always even if the data is not yet available. 我目前使用的方法是即使数据尚不可用也始终呈现表单。 This way there is no check needed for the loading prop as it presumes all data is available. 这样,装载道具就不需要检查,因为它假定所有数据都可用。 In case you do check for the loading prop and only render the form when the data has been loaded then the client browser waits with showing the form until data is available and this might feel annoying especially when it takes longer for the data to be available. 如果您确实检查了加载道具并且只在加载数据时呈现表单,那么客户端浏览器会等待显示表单直到数据可用,这可能会让人感到烦恼,尤其是当数据可用时间更长时。 Better let the browser use this spare time to build the form in the DOM already. 最好让浏览器利用这个空闲时间在DOM中构建表单。

But how to know the initial state from the graphql query and prefill? 但是如何从graphql查询和prefill中了解初始状态?

The state is used to keep the form input data while the initial data from the graphql source is NEVER loaded into the state! 状态用于保持表单输入数据,而来自graphql源的初始数据永远不会加载到状态! And every time the (truth) data is needed it will be recalculated. 每次需要(真值)数据时,都会重新计算。 This recalculation should happen at one place only so I introduced a helper function getTruthData(). 这个重新计算应该只在一个地方进行,所以我引入了一个辅助函数getTruthData()。 And wherever you need the truth data you let it handle by this helper function. 无论你需要真值数据,你都可以通过这个辅助函数来处理它。 Eg with the rendering and also with submitting the updated data back to graphql mutate. 例如,渲染以及将更新的数据提交回graphql mutate。 And only in this getTruthData() function it will be needed to return the data based on availability. 并且只有在此getTruthData()函数中才需要根据可用性返回数据。 Don't mind about performance as this recalculating the truth data is really fast and caching would only introduce unpredictable results due to the nature of React re-rendering. 不要介意性能,因为重新计算真实数据真的很快,缓存只会因React重新渲染的性质而引入不可预测的结果。

I've (tried to) update your code. 我(试图)更新你的代码。 What I did: 我做了什么:

  • remove componentWillReceiveProps as it will not be needed 删除componentWillReceiveProps,因为它不需要
  • remove the check for loading in the render 删除渲染中的加载检查
  • Introduce the getTruthData() 介绍getTruthData()
  • change from uncontrolled to controlled form ( I noticed later although not related to the question ) 从不受控制的形式变为受控制的形式(我后来注意到虽然与问题没有关系)

The code: 编码:

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>
    );
  }
}

The side effect might be that if the data from graphql will take really long that the form can be entered already. 副作用可能是如果来自graphql的数据需要很长时间才能输入表单。 With the controlled form you could check in the handleChange to only allow changes when the data is loaded properly. 使用受控表单,您可以检查handleChange以仅在正确加载数据时允许更改。 You could even de-activate the style of the button. 你甚至可以取消激活按钮的样式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM