skip to Main Content

I have function that gets the data from API request. I want to put this data to the state variable, but for some kind of reason it doesn’t work, when printing the state it always shows the empty array.

export default function Buttons({setOptionButtons, sendMessage, places}){

    const [data, setData] = useState(null)
    const [loading, setLoading] = useState(false)
    const [error, setError] = useState(null)
    const [names, setNames] = useState([])
    const [addresses, setAddresses] = useState([])

    useEffect(()=>{
        console.log(data)
    }
    ,[data])

    const pressOption = (input, option) => {
        const fetchData = async () => {
            for (const place of option){
                setLoading(true);
                try {
                    const requests = option.map((place) =>
                        axios.get(`url`)
                    );

                    const responses = await Promise.all(requests);
                    console.log(responses)
                    const items = responses.flatMap((response) => response.data.result.items);
                    const newNames = items.map((item) => item.name);
                    const newAddresses = items.map((item) => item.address_name);

                    setNames((prevNames) => [...prevNames, ...newNames]);
                    setAddresses((prevAddresses) => [...prevAddresses, ...newAddresses]);
                    setError(null);
                    console.log('Hello');
                    console.log(names)
                } catch(error) {
                    setError(error.message)
                } finally {
                    setLoading(false)
                }
            }
        };

        fetchData();
        setOptionButtons(false);
        sendMessage(input);
    }
    return (
///html code
    )
}

I tried to use promise.all and to call the printing outside the fetchData function. It should print data which I pass via useState.

2

Answers


  1. Chosen as BEST ANSWER

    It seems like the problem was the line setOptionButtons(false), which is asyncrhonous. So I added await to this line and shuffled it with updateOptionButtons(), so that it waits until first asynchronous function ends.

    Also, it is better to use Promise.all to handle multiple requests to API as I did here instead of using for-loop.

    export default function Buttons({setOptionButtons, sendMessage, places}){
    
        const [data, setData] = useState(null)
        const [loading, setLoading] = useState(false)
        const [error, setError] = useState(null)
        const [names, setNames] = useState([])
        const [addresses, setAddresses] = useState([])
    
        const updateOptionButtons = () => {
            setOptionButtons(false);
          };
    
    
        const pressOption = async (input, option) => {
            const urlArray = []
    
            for(let i = 0; i < option.length; i++){
                urlArray.push(urls_with_option[i])
            }
    
            let requestsArray = urlArray.map((url) => {
                let request = new Request(url);
                return request;
            });
         
    
            await Promise.all(requestsArray.map((request) => {
                return fetch(request).then((response) => {
                    return response.json();
                }).then((data) => {
                    return data;
                });
            })).then((values) => {
                let items = values[0].result.items;
                let names = items.map((item) => item.name);
                let addresses = items.map((item) => item.address_name);
                setData(values)
                setNames((prevNames)=> [...prevNames, ...names])
                setAddresses((prevAdaress)=> [...prevAdaress, ...addresses])
            }).catch(console.error.bind(console));
            
            await sendMessage(input);
            updateOptionButtons()
        }
    
        return (
          //Html code here
        )
    }


  2. I have taken the liberty of refactoring your code to the point where it should work, the issue I have discovered is that call to Array.map inside the for loop, this seems to bug out your state. You will find comments I have added across your function explaining why I have done what,

    function Buttons({ setOptionButtons, sendMessage, places }) {
      const [data, setData] = useState(null);
      const [names, setNames] = useState([]);
      const [addresses, setAddresses] = useState([]);
      
      const [error, setError] = useState(null);
      const [loading, setLoading] = useState(false);
    
      const onClick = useCallback(async (input, option) => {
        // early return if the data is already fetching
        if (!!loading) return;
        setError(null);
        setLoading(true);
        try {
          const promises = [];
    
          // more readable implementation of the for loop
          for (const place of option) {
            promises.push(axios.get("..."));
          }
    
          // Maybe use `Promise.allSettled` for better error handling
          const responses = await Promise.all(promises);
    
          const items = responses.flatMap((response) => {
            return response.data.result.items;
          });
    
          const newNames = items.map((item) => {
            return item.name;
          });
    
          const newAddresses = items.map((item) => {
            return item.address_name;
          });
    
          setNames((prev) => [...prev, ...newNames]);
          setAddresses((prev) => [...prev, ...newAddresses]);
        } catch (err) {
          setError(err.message);
        } finally {
          // handle all other updates directly inside the finally block
          setLoading(false);
          setOptionButtons(false);
          sendMessage(input);
        }
      }, [loading, sendMessage, setOptionButtons]);
    
      useEffect(() => {
        console.log(data);
      }, [data]);
    
      return <></>;
    }
    
    export default Buttons;
    

    Also you seem to be new to react, definitely take a look at the official documentation for react. They have just updated their entire documentation and it is really informative.

    PS:

    As the comments state above you will have pretty excessive requests to the API your calling, if you are paying per request or bandwidth I strongly recommend another refractor over that passage of code.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search