Jak zrobić wyszukiwarkę przy użyciu Apollo lazy query

Gdy React renderuje komponent, który wywołuje hook useQuery, klient Apollo automatycznie uruchamia zapytanie (query). Czasami trzeba zapytać o dane dotyczące jakiegoś zdarzenia. W tym celu można użyć haka useLazyQuery.

Gdy React renderuje komponent, który wywołuje hook useQuery, klient Apollo uruchamia zapytanie automatycznie, ale czasami trzeba zapytać o dane na podstawie jakiegoś zdarzenia. Świetnym przykładem jest komponent szybkiego wyszukiwania, który pozwala użytkownikom wyszukiwać produkty w sklepie eCommerce.

Istnieje hak useLazyQuery , który zwraca funkcję zapytania. Możesz użyć tej funkcji w dowolnym miejscu, a po jej uruchomieniu Apollo wykona dla Ciebie zapytanie.

Kiedy warto korzystać z leniwych zapytań?

Zazwyczaj niestandardowy hook Apollo useQuery jest używany, gdy chcesz zapytać o dane. Ten kook jest wywoływany, gdy React montuje i renderuje komponent, a klient Apollo automatycznie wykonuje wtedy zapytanie.

Co zrobić, gdy chcesz ręcznie wywołać zapytanie? Na przykład, gdy użytkownik kliknie określony przycisk lub jeśli uruchamiasz zapytanie w funkcji useEffect ?

Leniwe zapytania na ratunek!

W takim przypadku można użyć haka useLazyQuery. Jak wspomniałem wcześniej, jest to prawie to samo co useQuery z jednym wyjątkiem. Wywołanie useLazyQuery nie powoduje natychmiastowego wykonania powiązanego zapytania.

Zamiast tego, hook zwraca funkcję, którą można wywołać na żądanie.

Z drugiej strony, hook useLazyQuery jest idealny do wykonywania zapytań w odpowiedzi na różne zdarzenia, takie jak działania użytkownika. Ilekroć pojawi się polecenie useLazyQuery, aplikacja nie będzie natychmiast uruchamiać żadnych zapytań. Zapytanie należy uruchomić ręcznie.

Spójrz na przykład:

import React from 'react';
import { useLazyQuery } from '@apollo/client';
import { GET_SWEETIES } from './somewhere';

const MySweeties = () => {
  const [getSweeties, { loading, data }] = useLazyQuery(GET_SWEETIES);

  if (loading) return <p>Loading ...</p>;

  const shouldDisplaySweeties = data && data.sweeties ? 

    <img src={data.sweeties.image} alt="sweeties" /> : <button onClick={() => getSweeties({ variables: { type: 'chocolate' } })}>
    Get sweeties!
  </button>

  return shouldDisplaySweeties;
}

Jak bardzo jest to przydatne?

Refetch function

UseQuery zwraca dane, useLazyQuery również zwraca dane. Te hooki zwracają dane z pamięci podręcznej lub dane serwera. Nie ma to znaczenia, zwracają one żądane dane, a komponent renderuje te dane. Domyślnym zachowaniem haka useQuery jest wykonywanie zapytania podczas ponownego renderowania komponentu. Lazy query umożliwia wykonywanie zapytań na żądanie.

Co więcej, dodatkowy mechanizm pozwala na ponowne pobieranie zapytań na żądanie określonej akcji użytkownika. Spójrz na przykład:

const { loading, error, data, refetch } = useQuery(YOUR_QERY, {    variables: { sampleVar: 'abc'},  });

Funkcję refetch można powiązać w kodzie JSX w następujący sposób:

<button onClick={() => refetch({ sampleVar: 'xyz' })}>Refetch</button>

Jak widać, do funkcji można przekazywać nowe zmienne.

Praktyczne zastosowanie

Wykorzystajmy tę wiedzę o leniwych zapytaniach i zaimplementujmy komponent QuickSearch w aplikacji React.

Komponent QuickSearch

Tworzę komponent szybkiego wyszukiwania z następującą zawartością:

import React, { useState } from 'react';
import { Form, FormControl } from 'react-bootstrap';
import QuickSearchSuggestions from '../QuickSearchSuggestions';


const QuickSearch = () => {
    const [ isValid, setIsValid] = useState(false);
    const [ searchQuery, setSearchQuery ] = useState('');
    const handleChange = value => {
        const valueEntered = !!value;
        const isValid = valueEntered && value.length > 3;

        setIsValid(isValid);
        setSearchQuery(value);
    }

    return (
        <div className="justify-content-center d-flex position-relative">
            <Form inline className="w-100">
                <FormControl type="text" placeholder="Search entire shop" className="w-100" onChange={e => handleChange(e.target.value)}/>
            </Form>
            <QuickSearchSuggestions isValid={isValid} searchQuery={searchQuery}/>
        </div>
    );
};

export default QuickSearch;

Definiuję tam dwa stany: isValid i searchQuery i przekazuję je do komponentu podrzędnego. Sprawdzam długość wartości wprowadzonej przez użytkownika w polu wyszukiwania, a jeśli długość jest większa niż 3, wysyłam zapytanie.

Komponent sugestii szybkiego wyszukiwania

Tworzę nowy komponent o nazwie QuickSearchSuggestion z następującą zawartością:

import React from 'react';

import { ListGroup } from 'react-bootstrap';
import PropTypes from 'prop-types';
import useQuickSearchSuggestions from '../../hooks/useQuickSearchSuggestions';
import classes from './QuickSearchSuggestions.module.css'

const QuickSearchSuggestions = (props) => {
    const { isValid, searchQuery } = props;
    const {
        hasSuggestions,
        isLoading,
        isOpen,
        items
    } = useQuickSearchSuggestions({ isValid, searchQuery });

    const suggestions = items.map(product => {
        return <ListGroup.Item key={product.id}>
            {product.name}
        </ListGroup.Item>
    });

    const shouldDisplaySuggestions = suggestions ? <div className={classes.suggestions}>
        <ListGroup>
            {suggestions}
        </ListGroup>
    </div> : null;

    if (isOpen && hasSuggestions) {
        return shouldDisplaySuggestions;
    } else if (isLoading) {
        return <div className={classes.suggestions}>
            <ListGroup.Item>Loading...</ListGroup.Item>
        </div>;
    } else if (isOpen && !hasSuggestions) {
        return <div className={classes.suggestions}>
            <ListGroup>
                <ListGroup.Item>No products found</ListGroup.Item>
            </ListGroup>
        </div>
    } else {
        return null;
    }
};

QuickSearchSuggestions.propTypes = {
    isValid: PropTypes.bool.isRequired,
    searchQuery: PropTypes.string.isRequired
};

export default QuickSearchSuggestions;

Używam tam niestandardowego haka: useQuickSearchSuggestion. Zadaniem tego haka jest dostarczanie danych i logiki biznesowej dla komponentu. Zdefiniuję ten hook w następnym kroku.

Aby skończyć QuickSearchSuggestion, tworzę moduł CSS QuickSearchSuggestions.module.cssz następującymi stylami:

.suggestions {
    left: 0;
    position: absolute;
    top: 100%;
    right: 0;
    z-index: 10;
}

Użycie niestandardowego haka sugestii szybkiego wyszukiwania

import { useEffect, useState } from 'react';
import { useLazyQuery } from '@apollo/client';
import { GET_QUICK_SEARCH_SUGGESTIONS } from '../../queries/product.gql';
/**
 * The useQuickSearchSuggestions hook provides data and business logic for the QuickSearchSuggestions component
 *
 * @return {
 *  hasSuggestions {bool} - determines are products found based on provided search query
 *  isLoading {bool} - determines is data is currently loading
 *  isOpen {bool} - determines is component is opened
 *  items {array} - array with products returned from the API based on provided search query
 * }
 */
export const useQuickSearchSuggestions = props => {
    const { isValid, searchQuery } = props;
    const [ isOpen, setIsOpen ] = useState(false);
    const [ hasSuggestions, setHasSuggestions ] = useState(false);
    const [fetchSuggestions, { loading, data }] = useLazyQuery(GET_QUICK_SEARCH_SUGGESTIONS);

    useEffect(() => {
        if (isValid) {
            fetchSuggestions({
                variables: {
                    searchQuery
                }
            });
            setIsOpen(true);
        } else {
            setIsOpen(false);
        }
    }, [fetchSuggestions, isValid, searchQuery]);

    useEffect(() => {
        data && data.products && data.products.items && data.products.items.length ?
            setHasSuggestions(true): setHasSuggestions(false);
    }, [data]);

    return {
        hasSuggestions,
        isLoading: loading,
        isOpen,
        items: data && data.products && data.products.items ? data.products.items : []
    }
}

export default useQuickSearchSuggestions;

Jak widać, zdefiniowałem leniwe zapytanie, które zwraca funkcję fetchSuggestions .

Następnie używam tej funkcji w useEffect. Przed wywołaniem sprawdzam, czy zapytanie jest prawidłowe i aktualizuję flagę isOpen. Funkcja zapytania fetchSugestions pobiera zmienne graphql (searchQuery), pobiera dane i zwraca je do komponentu react .

Zapytanie graphQL

Ostatnią rzeczą, którą musimy zrobić, jest utworzenie zapytania graphQL używanego przez hak useQuickSearchSuggestions.

export const GET_QUICK_SEARCH_SUGGESTIONS = gql`
    query getQuickSearchSuggestions($searchQuery: String!) {
        products(search: $searchQuery) {
            items {
                id
                name
                small_image {
                    url
                }
                url_key
                url_suffix
                price {
                    regularPrice {
                        amount {
                            value
                            currency
                        }
                    }
                }
            }
        }
    }
`;

Podsumowanie

Wygląda na to, że funkcja szybkiego wyszukiwania działa dobrze:

Aby wykonywać zapytania GraphQL za pomocą klienta Apollo GraphQL, można wybrać jeden z dwóch niestandardowych hooków apollo react:

  1. useQuery
  2. useLazyQuery

Jaka jest różnica między hookami useQuery i useLazyQuery?

W przypadku użycia useLazyQuery nie jest wykonywane powiązane zapytanie. Zamiast tego funkcja zwraca funkcje zapytania w krotkach wyników wywoływanych podczas wykonywania zapytania.

Hak useLazyQery jest idealny, gdy na przykład trzeba pobrać dane do serwera GraphQL po akcji użytkownika, kliknięciu lub wpisaniu.

Hooki Apollo React i buforowane dane

Klient Apollo zapewnia mechanizm pamięci podręcznej o nazwie InMemoryCache. Dzięki temu apollo buforuje wyniki zapytań w pamięci. Oczywiście haki Apollo react obsługują tę pamięć podręczną. Domyślne zasady pobierania można ustawić globalnie i lokalnie dla każdego zapytania.

Obsługa błędów GraphQL

Leniwa obsługa błędów zapytań jest taka sama, jak w przypadku korzystania z haka useQuery . Zobacz ten artykuł jeśli chcesz dowiedzieć się więcej o błędach GraphQL.

Udostępnij post:

Możesz także polubić

Kariera w branży technologicznej: Jak rozwijać swoje umiejętności

Jesteś programistą i chciałbyś się rozwijać? W internecie znajdziesz pełno materiałów o tym, jak to zrobić. Pomimo tego nie uciekaj — mam coś, co Cię zaciekawi. Czy wiesz, że Adam Małysz — legendarny polski skoczek, zanim został mistrzem latania, to był dekarzem? Nie śmiem się porównywać z Panem Adamem, natomiast są dwie rzeczy, które nas łączą.

Ja też byłem dekarzem i też udało mi się przebranżowić. Może nie w tak spektakularny sposób, ale jednak. W tym artykule podzielę się z Tobą moim osobistym doświadczeniem, które zdobyłem na drodze od dekarza przez programistę do tech leada i dam Ci wskazówki, które będziesz mógł zastosować, aby się rozwijać i awansować, a może nawet zmienić diametralnie swoją karierę.

Czytaj więcej
AHA stack przywróćmy prostotę frontendu

AHA! Przywróćmy prostotę Frontendu

Czy zastanawiałeś się, dlaczego w dzisiejszych czasach, gdy mamy dostęp do najnowszych technologii i rozwiązań, projekty IT nadal kończą się fiaskiem? Czy nie uważasz, że w wielu przypadkach zamiast upraszczać to komplikujemy sobie życie i pracę? Czasami mniej znaczy więcej, zwłaszcza w świecie frontendu! Czytaj dalej i dowiedz się czym jest AHA stack i jak robić frontend prościej.

Czytaj więcej