0

I'm working on a React project that includes a dropdown menu of films to choose from. When I select a film it calls the function filmSelected:

const [selectedFilm, setSelectedFilm] = useState({})

const fetchCharacters = async (l, f) => {
    // loops through list (l) and fetches character info from api
    let c = 0;
    let characters = [];
    for (let i = 0; i < l.length; i++) {
        try {
          const charFetch = await fetch(l[i]);
          const response = await charFetch.json();
          characters.push(response);
          c++;
          setCount(c);
        } catch (e) {
          return e;
        }
      }
    return characters;
  };

const filmSelected = async (film) => {
    // sets the selected film state to the film selected in dropdown menu
    // fetches character info from api
      setSelectedFilm(film)
      const c = await fetchCharacters(film.characters, film);
      setCharacters(c);
  };

If I were to select a different film before the for loop completes, it creates a problem and almost seems like the function's for loop is being executed simultaneously with the new call. I'm trying to figure out a way to exit the for loop if the film changes before the for loop has been fully executed.

I'm using useState and setting the selectedFilm state within the function, but it doesn't set the state it fast enough to check if the film passed through the fetchCharacters function and the state of selectedFilm are the same.

Any suggestions would be greatly appreciated. Thank you in advance!

Kevin

Kevin G
  • 168
  • 2
  • 15

2 Answers2

0

Assuming you are using axios for making API request. ( this approach you may use with any library, the way may be different).

You've to cancel the previous request before making new one.

you can easily do that in axios.

// declare an ajax request's cancelToken (globally)
let ajaxRequest = null; 

function getComments() {

    // cancel  previous ajax if exists
    if (ajaxRequest ) {
        ajaxRequest.cancel(); 
    }

    // creates a new token for upcomming ajax (overwrite the previous one)
    ajaxRequest = axios.CancelToken.source();  

    return axios.get('/api/get-comments', { cancelToken: ajaxRequest.token }).then((response) => {
        console.log(response.data)
    }).catch(function(err) {
        if (axios.isCancel(err)) {
           console.log('Previous request canceled, new request is send', err.message);
        } else {
               // handle error
        }
    });
}

For more detail on it, you may google axios cancel token or visit

Rahul
  • 5,594
  • 7
  • 38
  • 92
0

In my opinion you have 2 options

  1. you could block the film dropdown until all characters are loaded (not my prefered solution)
  2. You could use a variable outside like let loadCharacter = true and set it to false when you click on a new film. In fetchCharacters you can conditional return to break the loop. Initial in fetchCharacters you can set it to true again.

For example:

const [selectedFilm, setSelectedFilm] = useState({})
let loadCharacters = true;
const fetchCharacters = async (l, f) => {
    // loops through list (l) and fetches character info from api
    loadCharacters = true;
    let c = 0;
    let characters = [];
    for (let i = 0; i < l.length; i++) {
        if (!loadCharacters) return;
        try {
          const charFetch = await fetch(l[i]);
          const response = await charFetch.json();
          characters.push(response);
          c++;
          setCount(c);
        } catch (e) {
          return e;
        }
      }
    return characters;
  };

const filmSelected = async (film) => {
    // sets the selected film state to the film selected in dropdown menu
    // fetches character info from api
      loadCharacters = false;
      setSelectedFilm(film);
      const c = await fetchCharacters(film.characters, film);
      setCharacters(c);
  };
MrDeibl
  • 157
  • 1
  • 10