Using React hooks in an external API service layer

Viewed 1744

I'm building a react app for an eCommerce site. I have a page for Product Search. I'm using the SWR library (useSWR hook), because I like it's caching ability. However, as it uses hooks, it seems I can't place this call outside of my function component. Ideally, I'd like to segment off all API functionality into a separate library / service layer.

Let me demonstrate what I'm trying to do with some extremely cut-down code:

import React, { useState } from 'react';
import { useParams } from "react-router";
import * as ProductAPI from 'common/libs/ProductAPI';

function ProductSearch() {

    const [searchResults, setSearchResults] = useState(null);

    let { keywords } = useParams();

    const results = ProductAPI.getProductSearchResults(keywords);

    if (searchResults != results)
        setSearchResults(results);

    function renderSearchResults() {
        ...
    }

    return (
        <>
            {renderSearchResults()}
        </>
    );
}

export default ProductSearch;

And the API library:

import useSWR from 'swr';

var productSearchURL = process.env.REACT_APP_API_ENDPOINT + '/product/search';

export function getProductSearchResults(keywords) {

    let url = productSearchURL;
    url += "/" + encodeURI(keywords);

    const { data } = useSWR(url);

    return data;
}

Now, React is giving met the "you're breaking the rules of hooks" error:

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

I've been struggling to find a solution that would let me achieve this abstraction of API functionality into it's own function/component.

If there's no way to solve this using my current approach, what would be the best way to achieve this layered approach to the API implementation?

1 Answers

You should create a custom hook that uses useSWR to fetch data. React will allow to create a custom hook with other hooks used inside it. Somthing like this:

export function useProductSearchResults(keywords) {

    let url = productSearchURL;
    url += "/" + encodeURI(keywords);

    const { data } = useSWR(url);

    return data;
}

then use this custom hook in you code:

.
.
 let { keywords } = useParams();

    const results = useProductSearchResults(keywords);

    if (searchResults != results)
        setSearchResults(results);
.
.
.
Related