1

Here is a very basic search by title query on a public API. In my actual app my API calls are under services.js files so I'm trying to do this where my API call is not inside the react component.

https://codesandbox.io/s/elastic-pond-pghylu?file=/src/App.js

import * as React from "react";
import axios from "axios";

// services.js
const fetchPhotos = async (query) => {
  const { data } = await axios.get(
    `https://jsonplaceholder.typicode.com/photos?title_like=${query}`
  );
  return data;
};

export default function App() {
  const [photos, setPhotos] = React.useState([]);
  const [searchTerm, setSearchTerm] = React.useState("");

  const fetch = React.useCallback(async () => {
    const data = await fetchPhotos(searchTerm);
    setPhotos(data);
  }, [searchTerm]);

  React.useEffect(() => {
    fetch();
  }, [fetch]);

  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
      />
      <div>
        {photos?.map((photo) => (
          <div>{JSON.stringify(photo.title)}</div>
        ))}
      </div>
    </div>
  );
}

The problem with my code is this (too many api calls while im typing):

enter image description here

My attempt to fix this

  • I tried cancelToken. This cancels my previous request. I was able to implement this but in my actual API the get request is so fast that it still manages to finish the request. So I'm trying to do this without cancelToken.
  • I recently came across debouncing and it seems to do what I need i'm just struggling to get it to work

for example, I tried this: (I wrapped debounce onto my fetchPhotos function)

import {debounce} from 'lodash';

// services.js
const fetchPhotos = debounce(async (query) => {
  const { data } = await axios.get(
    `https://jsonplaceholder.typicode.com/photos?title_like=${query}`
  );
  return data;
}, 500);

however now fetchphotos returns undefined always?

isherwood
  • 58,414
  • 16
  • 114
  • 157
user349557
  • 83
  • 1
  • 12

1 Answers1

0

You can make use of useCallback so that the debounced (fetchPhotos) will have same function across re-renders

import * as React from "react";
import axios from "axios";
import { debounce } from "lodash";

// services.js

export default function App() {
  const [photos, setPhotos] = React.useState([]);
  const [searchTerm, setSearchTerm] = React.useState("");

  async function fetchData(searchTerm) {
    const data = await axios.get(
      `https://jsonplaceholder.typicode.com/photos?title_like=${searchTerm}`
    );
    setPhotos(data.data);
  }
  const debounced = React.useCallback(debounce(fetchData, 500), []);

  React.useEffect(() => {
    // for the first render load
    fetchData("");
  }, []);

  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => {
          setSearchTerm(e.target.value);
          debounced(e.target.value, 1000);
        }}
      />
      <div>
        {photos?.map((photo) => (
          <div key={photo.id}>{JSON.stringify(photo.title)}</div>
        ))}
      </div>
    </div>
  );
}

Edit zealous-tu-g6iop7

KcH
  • 3,302
  • 3
  • 21
  • 46