8

We are using react-select and fetching the items as the user types. I am not able to make it work with react-apollo.

Can someone help me provide a guideline?

Here is my unsuccessful attempt:

class PatientSearchByPhone extends Component {
  updateProp = mobile => {
    if (mobile.length < 10) return;
    this.props.data.refetch({ input: { mobile } });
  };

  render() {
    console.log(this.props.data);
    return <AsyncSelect cacheOptions loadOptions={this.updateProp} />;
  }
}

const FETCH_PATIENT = gql`
  query Patient($input: PatientSearchInput) {
    getPatients(input: $input) {
      id
      first_name
    }
  }
`;
export default graphql(FETCH_PATIENT, {
  options: ({ mobile }) => ({ variables: { input: { mobile } } })
})(PatientSearchByPhone);

Versions:
"react-apollo": "^2.1.11",
"react-select": "^2.1.0"

Thanks for your time.

Nishant
  • 54,584
  • 13
  • 112
  • 127

2 Answers2

19

I got an e-mail asking a response to this question. It reminds me of this XKCD comics:

Never have I felt so close to another soul

I do not recall the exact solution I implemented, so I setup a complete example for this.

This app (code snippet below) kickstarts searching as soon as you type 4 characters or more in the input box (You are expected to type artist's name. Try vinci?). Here is the code:

import React, { useState } from "react";
import "./App.css";
import AsyncSelect from "react-select/async";
import ApolloClient, { gql } from "apollo-boost";

const client = new ApolloClient({
  uri: "https://metaphysics-production.artsy.net"
});

const fetchArtists = async (input: string, cb: any) => {
  if (input && input.trim().length < 4) {
    return [];
  }
  const res = await client.query({
    query: gql`
      query {
        match_artist(term: "${input}") {
          name
          imageUrl
        }
      }
    `
  });

  if (res.data && res.data.match_artist) {
    return res.data.match_artist.map(
      (a: { name: string; imageUrl: string }) => ({
        label: a.name,
        value: a.imageUrl
      })
    );
  }

  return [];
};

const App: React.FC = () => {
  const [artist, setArtist] = useState({
    label: "No Name",
    value: "https://dummyimage.com/200x200/000/fff&text=No+Artist"
  });
  return (
    <div className="App">
      <header className="App-header">
        <h4>Search artists and their image (type 4 char or more)</h4>
        <AsyncSelect
          loadOptions={fetchArtists}
          onChange={(opt: any) => setArtist(opt)}
          placeholder="Search an Artist"
          className="select"
        />
        <div>
          <img alt={artist.label} src={artist.value} className="aimage" />
        </div>
      </header>
    </div>
  );
};

export default App;

You can clone https://github.com/naishe/react-select-apollo it is a working example. I have deployed the app here: https://apollo-select.naishe.in/, may be play a little?

Nishant
  • 54,584
  • 13
  • 112
  • 127
0

The other option is to execute the graphql query manually using the client that is exposed by wrapping the base component with withApollo.

In the example below, we have,

  1. BaseComponnent which renders the AsyncSelect react-select component
  2. loadOptionsIndexes which executes the async graphql fetch via the client
  3. BaseComponent.propTypes describes the required client prop
  4. withApollo wraps the base component to give us the actual component we'll use elsewhere in the react app.
const BaseComponent = (props) => {
  const loadOptionsIndexes = (inputValue) => {
    let graphqlQueryExpression = {
      query: QUERY_INDEXES,
      variables: {
        name: inputValue
      }
    }

    const transformDataIntoValueLabel = (data) => {
      return data.indexes.indexes.map(ix => { return { value: ix.id, label: ix.name }})
    } 

    return new Promise(resolve => {
      props.client.query(graphqlQueryExpression).then(response => {
        resolve(transformDataIntoValueLabel(response.data))
      })
    });

  }

  return (
    <>
      <div className="chart-buttons-default">
        <div className="select-index-input" style={{width: 400, display: "inline-block"}}>
          <AsyncSelect 
            isMulti={true}
            cacheOptions={true}
            defaultOptions={true}
            loadOptions={loadOptionsIndexes} />
        </div>
      </div>
    </>
  )
}

BaseComponent.propTypes = {
  client: PropTypes.any,
}

const ComplementComponent = withApollo(BaseComponent);

Sorry if the example is a little off - copy and pasted what I had working rather than moving on without giving back.

westonplatter
  • 1,475
  • 2
  • 19
  • 30