简体   繁体   中英

I have an array of different IDs and I want to fetch data from each IDs . Is it possible?

I have an array of mongoDB ids.

const pId =  ['62b3968ad7cc2315f39450f3', '62b37f9b99b66e7287de2d44']

I used forEach to seperate the IDs like:

pId.forEach((item)=>{
console.log(item)
})

but I have a database(products) from where I want to fetch data from. So I tried

   const [product, setProduct] = useState([{}]);
useEffect(() => {
            pId?.forEach((item) => {
                const getProduct = async () => {
                    try {
                        const res = await userRequest.get("/products/find/" + item)
                        setProduct(res.data)
                    } catch (err) {
                        console.log(err)
                    }
                }
                getProduct()
            })
        }, [pId])

I used useState[{}] because I want to collect the data in an array of objects.

I used useState[{}] because I want to collect the data in an array of objects.

Your code isn't collecting objects into an array. It's setting each result of the query as the single state item (overwriting previous ones).

If you want to get all of them as an array, build an array; one way to do that is map with the map callback providing a promise of each element, then use Promise.all to wait for all of those promises to settle:

// The usual advice is to use plurals for arrays, not singulars ("products", not "product")
const [products, setProducts] = useState([]); // Start with an empty array
useEffect(() => {
    if (pId) {
        Promise.all(pId.map((item) => userRequest.get("/products/find/" + item)))
            .then((products) => setProducts(products))
            .catch((error) => console.log(error));
    }
}, [pId]);

Note that if pId changes while one or more userRequest.get calls are still outstanding, you'll get into a race condition. If userRequest.get provides a way to cancel in-flight calls (like fetch does), you'll want to use that to cancel the in-flight calls using a cleanup callback in the useEffect . For example, if userRequest.get accepted an AbortSignal instance (like the built-in fetch does), it would look like this:

const [products, setProducts] = useState([]);
useEffect(() => {
    const controller = new AbortController();
    const { signal } = controller;
    if (pId) {
        Promise.all(pId.map((item) => userRequest.get("/products/find/" + item, { signal })))
            .then((products) => setProducts(products))
            .catch((error) => {
                if (!signal.aborted) {
                    console.log(error);
                }
            });
    }
    return () => {
        controller.abort();
    };
}, [pId]);

Again, that's conceptual; userRequest.get may not accept an AbortSignal , or may accept it differently; the goal there is to show how to cancel a previous request using a useEffect cleanup callback.

You can map through the ids and create a promise for each, then use Promise.all() and at last set the products state:

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

const Example = () => {
  const [products, setProducts] = useState([]);
  const ids = ['62b3968ad7cc2315f39450f3', '62b37f9b99b66e7287de2d44']

  useEffect(() => {
    if(ids) Promise.all(ids.map(id => userRequest.get("/products/find/" + id).then(r => r.data))).then(results => setProducts(results))
  }, [ids])
}

I renamed some of the variables for clarity to the future visitors. ( pId to ids and product to products ).

You're overwriting your product array with an individual result from each request. A simple solution would be to append to the array instead:

setProduct(product => [...product, res.data]); // take the old array and append the new item

As TJ Crowder rightly suggested in the comments, you would keep appending to the initial product state when using the simple setter, so you need to use callback form which receives the current state as a parameter and returns the update.

I suggest you rename that particular state to products / setProducts to make clear it's an array.

Not directly related to the question, but bear in mind that firing a huge number of individual requests may cause performance degradation on the client and backend; there are plenty of options to deal with that, so I am not going into more detail here.

yes, it's possible. just change your code a bit:

const [product, setProduct] = useState([]); // empty array is enough for initialzing

useEffect(() => {

 async function doSomethingAsync() {
            
            if(!pId) return;
            let newArray = [];
            for(let item of pId) {

              try {
                        const res = await userRequest.get("/products/find/" + item)
                        newArray.push(res.data);  // push data to temporary array
                    } catch (err) {
                        console.log(err)
                    }
            }

            // set new state once
            setProduct(newArray);

 }
doSomethingAsync();

}, [pId])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM