4

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 Answer 1

4

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);
.
.
.
Sign up to request clarification or add additional context in comments.

2 Comments

Urgn. I had tried an approach like that, however: it worked in development, but failed when I deployed to production (using Visual Studio deployment tool). The reason, I found out, is that I wasn't naming the custom hook starting with "use". So yeah, modifying to start with "use" works - shitty that I can't specify my own naming convention, but I guess there's not much I can do. Thanks for your help!
Glad I cound help. The 'use' prefix I guess is important to identify normal functions from custom hook functions. Gives React a way to enforce rules to make hooks work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.