Concurrent React JS requests with queued AXIOS, without using Promise.all or Promise.allSettled

Asked

Viewed 25 times

1

I have the following situation: a form with a single input and a list with results that will be displayed on screen. The idea is for the user to enter a code in the input, and when changing the input value a query should be performed using AXIOS, through the GET method. This query should be performed every time the input value is modified, thus being able to generate several requests in parallel, but generated one after the other, and not generated simultaneously. See the initial code to better explain the problem:

import React, { useState, useEffect } from 'react';
import axios from 'axios';


export default function App() {
  const [id, setId] = useState(0);
  const [users, setUsers] = useState([]);

  const addUser = (user) => {
    setUsers([user, ...users]);
  }


  const onChangeInput = e => {
    setId(e.target.value);
  };

  const getUser = userId => {
    return axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`);
  };

  useEffect(() => {
    if (id) {
      getUser(id)
      .then(response => {
        addUser(response.data)
      })
      .catch(err => console.error(err))
    }
  }, [id]);

  

  return (
    <>
      <div>
        <input type="number" value={id} onChange={e => onChangeInput(e)} />
      </div>

      <ul>
        {
          users.map(u => <li key={u.id}>{ u.name }</li> )
        }
      </ul>
    </>
  );
}

The problem in this code is that when performing a query via GET, and executing a next query before the first or previous one ends, I lose the REPLY from the previous request. In this example, just go changing the input codes by clicking on the "up arrow" to change the codes quickly (quickly passing the numbers 1,2,3,4 ...) and notice the result. If we see the first 5 users quickly, some will not be displayed (depending on how fast the changes were and response time for each of them)

Well, I would like every time I modified the input value and generated a request for each change to be displayed on the screen the information "LOADING..." until the RESPONSE is actually loaded, and in parallel with other requests. For this, I made some modifications, arriving at the following situation:

import React, { useState, useEffect } from 'react';
import axios from 'axios';


export default function App() {
  const [id, setId] = useState(0);
  const [userResponses, setUserResponses] = useState([]);


  const addUserResponse = (userLoader) => {
    setUserResponses([userLoader, ...userResponses]);
  }

  const onChangeInput = e => {
    setId(e.target.value);
  };

  const getUser = userId => {
    return axios.get(`https://jsonplaceholder.typicode.com/users/${userId}`);
  };

  const searchUser = (userId) => {
    let userResponse = {
      id: userId,
      loading: true,
      data: null,
      error: null,
      promise: 
        getUser(userId)
        .then(response => {
          console.log("this", this);
          this.loading = false;
          this.data = response.data;
          return this.data;
        })
        .catch(err => {
          console.error(err);
          this.error = err;
        })
    }

    return userResponse;

  }

  useEffect(() => {
    if (id) {
      let ur = searchUser(id);
      addUserResponse(ur);
    }
  }, [id]);


  return (
    <>
      <div>
        <input type="number" value={id} onChange={e => onChangeInput(e)} />
      </div>

      <ul>
        {
          userResponses.map(u => <li key={u.id}>{ u.loading ? "loading... " : u.data.name }</li> )

        }
      </ul>
    </>
  );
}

In this implementation I created the searchUser method that returns an object in the following format:

let userResponse = {
  id: ...,
  loading: ...,
  data: ...,
  error: ...,
  promise: ...
}

As I execute a REQUEST inside the attribute "Promise", I try to change the values of loadint, date and error using the keyword "this", but in this code snippet it looks like "Undefined".

In list rendering, snippet with the following code

userResponses.map(u => <li key={u.id}>{ u.loading ? "carregando... " : u.data.name }</li> )

always appears "LOADING.", making sense, since there was error in the execution of the method serachUser because "this" is like "Undefined".

Well, could you help me how to resolve this situation? or some more elegant alternative?

The above examples can be seen in:

https://stackblitz.com/edit/react-vkwv2m

https://stackblitz.com/edit/react-3rwwdv

Obs 1: this is an example I simplified here, so I don’t have to display such a complex and exhaustive code.

Note 2: I used an end point for jsonplaceholder testing (https://jsonplaceholder.typicode.com/users/:id), and only has 10 ids, with values from 1 to 10, enough for the demonstration.

  • Just so we’re clear, do you really want him to do this much research? You don’t want him to cancel the ones that weren’t completed and also use a mockery?

  • In reality, I would display more information than this, in a table, but also each request would not start as quickly as the user will type a 7-digit code and press enter to start the request. It wouldn’t be as fast as just clicking and generating sequential code like the example above. In the original implementation, I do a Cancel but only if the user reports the same code more than once, then I cancel the previous request if it is not finished. Information displayed for all requests is important for user analysis and comparison.

No answers

Browser other questions tagged

You are not signed in. Login or sign up in order to post.