简体   繁体   中英

How to implement Search Function in React-GraphQL using React-Hooks and Apollo Client?

I try to implement the Search function into my management system using React-Hooks and GraphQL-Apollo Client. While the interface is shown successfully, when I press the 'Search' button it came out an error which named:

Invalid hook call. Hooks can only be called inside of the body of a function component.

I'm pretty sure the useQuery is being called inside a function, so I do not understand what will cause this. The other function such as display all users and add new users are working fine.

I have tried a couple of ways to implement the search function and search around online while still couldn't get it solve. This is my first time encounters React-Hooks too.

Here is my current code in the searchUser component

import React from 'react'
import {useQuery} from 'react-apollo-hooks';
import {searchUserQ} from '../queries/queries';

const SearchUserForm = () => {
    let name = '';
    let content;

return (
    <div id="edit-user">
        <div className="field">
            <label htmlFor="name">Search UserName</label>
            <input type="text" id="name" onChange={ (event) => {
                name = event.target.value }}/>
            <button onClick={(event) => {
                event.preventDefault();
                const { data, loading, error } = useQuery(searchUserQ, {
                    variables: { name: name },
                    suspend: false
                });

                if (loading) {
                    content = <p>Loading User...</p>
                }

                if (error){
                    console.log(`Error Occur: ${ error }`);
                    content = <p>Error Occur!</p>
                }

                content = data.users.map(user => (
                    <p key={user.id}>Username: {user.name}    ID: {user.id}</p>
                ));
            }}>
                Submit
            </button>
            <p>{ content }</p>
        </div>
    </div>
)
}

export default SearchUserForm;

Can anyone help with this?

One more question is that my data seems to return undefined everytime I execute the query. Is there something wrong with my query? Here are the query:

const searchUserQ = gql`
query User($name: String!){
    user(name: $name){
        id
        name
        email
    }
 }
`;

Thanks and appreciate on the help!

According to the Rules of Hooks :

Don't call Hooks from regular JavaScript functions. Instead, you can:

✅ Call Hooks from React function components.

✅ Call Hooks from custom Hooks (we'll learn about them on the next page).

If you need to manually call a query manually ins response to an event, use the Apollo client directly. You can use useApolloClient to get an instance of the client inside your component:

const SearchUserForm = () => {
  const client = useApolloClient();
  ...
    return (
      ...
      <button onClick={async (event) => {
        try {
          const { data } = client.query({
            query: searchUserQ,
            variables: { name: event.target.value },
          });
          // Do something with data, like set state
        catch (e) {
          // Handle errors
        }
      }} />

You can also still use useQuery , like this:

const SearchUserForm = () => {
  const [name, setName] = useState('')
  const { data, loading, error } = useQuery(searchUserQ, {
    variables: { name },       
  });
  ...
    return (
      ...
      <button onClick={async (event) => {
        setName(event.target.value)
        ...
      }} />

You can use the useLazyQuery method and expose your data object to your entire component.

import {useLazyQuery} from '@apollo/react-hooks';
// - etc. -

const SearchUserForm = () => {
 // note: keep the query hook on the top level in the component
 const [runQuery, { data, loading, error }] = useLazyQuery(searchUserQ);

 // you can now just use the data as you would regularly do: 
 if (data) {
   console.log(data);
 }

 return (
   <div id="edit-user">
     <div className="field">
        <label htmlFor="name">Search UserName</label>
        <input
          type="text"
          id="name"
          onChange={(event) => {name = event.target.value }} />
        <button onClick={
          (event) => {
            event.preventDefault();
            // query is executed here
            runQuery({
              variables: { name }, // note: name = property shorthand
              suspend: false
            })
          }}>
          // - etc. -
 );
}

As opposed to doing the useQuery , the useLazyQuery method will only be executed on the click .

At the point where you are able to pass the 'name' value as a parameter.

If for example you would use the useQuery instead, and have a parameter that is required in your query (ie String! ), the useQuery method will provide you with an error. Because on component render it will try to run that query directly without the required parameter because at that period of time it's not available yet.

I found problem to my second answer, should just wrap an if-statement before executing it, here is the complete code:

import React, {useState} from 'react'
import {useQuery} from 'react-apollo-hooks';
import {searchUserQ} from '../queries/queries';

const SearchUserForm = () => {

const [name, setName] = useState('');
const { data, error, loading } = useQuery(searchUserQ, {
    variables: { name }
});

let content;
let sName;

if (data.user !== undefined && data.user !== null) {
    if (loading) { content = 'Loading User...' }

    if (error) { content = `Error Occur: ${error}` }

    const user = data.user;
    content = `Username: ${ user.name }    ID: ${ user.id }`
}

return (
    <div id="edit-user">
        <div className="field">
            <label htmlFor="name">Search UserName</label>
            <input type="text" id="name" onChange={(event) => {
                sName = event.target.value;
            }}/>
            <button onClick={(event) => {
                setName(sName);
            }} value={name}>
                Search
            </button>
        </div>
        <div>
            <p>{ content }</p>
        </div>
    </div>
)

};

export default SearchUserForm;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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