在类组件的方法中获取查询

Posted

技术标签:

【中文标题】在类组件的方法中获取查询【英文标题】:Fetching query in method of a class component 【发布时间】:2020-01-12 10:43:26 【问题描述】:

带有反应的 Apollo 客户端 (2.6.3)。 是否可以在类组件的方法中获取数据? 我正在构建一个全局搜索组件,并且我只想在输入第三个(以及每个后续)字符时获取数据。现在它是用 fetch api 实现的,但我想切换到 apollo 客户端和 graphql api。

到目前为止,由于我使用的是 apollo 客户端 API,因此我还没有遇到任何问题 <Query/> 组件。

我尝试使用新的 hooks api,但结果发现 useLazyQuery() 只能在函数组件中使用(钩子规则)。

这是我到目前为止所做的。我知道那个组件很乱,我愿意接受建议。这是我的第一个 react 应用。

import React,  Component  from "react";
import PropTypes from "prop-types";
import  Select, Icon, Button  from "antd";
import  es  from "../../../utils/queries";
import  useLazyQuery  from "@apollo/react-hooks";

const  Option, OptGroup  = Select;

class GlobalSearch extends Component 
  constructor(props) 
    super(props);
    this.searchInput = React.createRef();
  

  state = 
    data: [],
    value: undefined,
    display: false
  ;

  componentDidMount() 
    document.addEventListener("click", this.onClickOutside);
  

  generateOptions(data) 
    return data.map(d => 
      if (d._index === "cases") 
        d.label = (
          <div>
            <Icon style= fontSize: "18px"  type="tool" />
            <span style= color: "#183247" >
              d._source.number + d._source.serialNumber
            </span>
          </div>
        );
       else if (d._index === "items") 
        d.label = (
          <div>
            <Icon style= fontSize: "18px"  type="barcode-o" />
            <span style= color: "#183247" >d._source.name</span>
          </div>
        );
       else if (d._index === "people") 
        d.label = (
          <div>
            <Icon
              type="user"
              style= fontSize: "18px", float: "left", marginTop: "3px" 
            />
            <div style= marginLeft: "26px" >
              <span style= color: "#183247" >
                " "
                <div>d._source.name + " " + d._source.surname</div>
              </span>
              <div>d._source.email</div>
            </div>
          </div>
        );
       else if (d._index === "counterparties") 
        d.label = (
          <div>
            <Icon style= fontSize: "18px"  type="shop" />
            <span style= color: "#183247" >d._source.name</span>
          </div>
        );
       else 
        d.label = "undefined";
      
      return d;
    );
  

  //group data according to type of the search result (index e.g. cases, items, contacts)
  setData = es_data => 
    const data = ;
    const map = new Map();
    for (const item of es_data) 
      if (!map.has(item._index)) 
        map.set(item._index);

        data[item._index] = [];
      
      data[item._index].push(item);
    

    this.setState( data: data );
  ;

  handleSearch = value => 
    if (value.length > 2) 
      //tutaj wyszukujemy dane na serwerze a wyniki callbackiem przesyłamy do state.data[]
      //response.json() calls mixin methods from body
      
      const host = window.location.hostname

      // const  loading, data  = useLazyQuery(es(value));
      // if (loading) return undefined;

      // if (data) 
      //   this.setData(this.generateOptions(data));
      // 

      fetch(`http://$host:3000/api/es?searchString=$value`)
        .then(response => response.json())
        .then(es_data => 
          this.setData(this.generateOptions(es_data));
        );
    
  ;

  handleChange = value => 
    //przy kazdym wpisanym znaku aktualizuj wartosc pola
    this.setState( value );
  ;

  handleSelect = (key, option) => 
    console.log(key);
    console.log(option);
  ;

  handleBlur = e => 
    this.setState( display: false, value: undefined, data: [] );
  ;

  onClick = () => 
    this.setState( display: !this.state.display );
  ;

  getSearchField(options) 
    if (this.state.display) 
      return (
        <Select
          id="global-search-field"
          optionLabelProp="label"
          showSearch
          value=this.state.value
          placeholder="search..."
          style= display: this.state.display, width: "350px" 
          defaultActiveFirstOption=true
          showArrow=false
          filterOption=false
          onSearch=this.handleSearch
          onChange=this.handleChange
          onSelect=this.handleSelect
          notFoundContent="nothing here..."
          onBlur=this.handleBlur
          autoFocus=true
          showAction=["focus"]
          dropdownClassName="global-search-dropdown"
        >
          options
        </Select>
      );
    
  

  render() 
    //generate options for each group (index)
    const options = Object.keys(this.state.data).map(d => (
      <OptGroup label=d>
        this.state.data[d].map(d => (
          <Option
            className=d._index
            type=d._index
            key=d._id
            label=d.label
          >
            <div>d.label</div>
          </Option>
        ))
      </OptGroup>
    ));

    return (
      <span>
        this.getSearchField(options)

        <Button
          id="global-search"
          shape="circle"
          icon="search"
          onClick=this.onClick
        />
      </span>
    );
  


export default GlobalSearch;

【问题讨论】:

【参考方案1】:

Apollo Client 提供了一个基于 Promise 的 API,它提供了对运行查询/突变的时间/地点的更多命令式控制。以下是来自their docs 的示例:

client
  .query(
    query: gql`
      query GetLaunch 
        launch(id: 56) 
          id
          mission 
            name
          
        
      
    `,
    variables:  ... 
  )
  .then(result => console.log(result));

您可以导入您的客户端并在任何方法或函数中使用此模式,就像使用 Promises 的任何其他异步操作一样。

【讨论】:

您知道如何获取 400 错误请求返回的错误的 json 响应吗?

以上是关于在类组件的方法中获取查询的主要内容,如果未能解决你的问题,请参考以下文章

如何在类组件中使用 next.js 中的“useRouter()”?

Ruby - 在类中获取非祖先方法的数组

无法在异步方法中获取查询,无法从外部导入 graphql 查询

按名称获取组件不支持所有操作?

在类之间存储和传递数据的正确方法

为啥在类构造函数中使用 setState 方法时 React 会抛出错误?