简体   繁体   中英

Render components before others when results are returned

I have a function that fetches from an API with four endpoints; one that returns the data (from another API) and three others that do some analysis on text. I first call the first endpoint (/getdata), which returns the data plus a cleaned text attribute. I then use the clean text to call the other three endpoints.

My problem is, the three analysis calls take considerably more time to complete. I want to fetch the data, display all components related to the data only (no analysis), then after the other 3 calls complete (there will be an indication like a loading animation on the components, etc. if possible), the components related to them are populated.

What I have:

// this is set through a form
const [text, setText] = useState('');

const getData = useCallback(async () => {
    const url = `https://my-api.com/api/getdata?text=${text}`;
    console.log(url);
    const response = await fetch(url);
    const data = await response.json();

    return data;
}, [text]);

const getOtherData = async (data) => {
    const newData = [];
    for (const tweet of data.results) {
      const url1 = `https://my-api.com/api/getsentiment?text=${tweet.clean_text}`;
      const url2 = `https://my-api.com/api/getkeywords?text=${tweet.clean_text}`;
      const url2 = `https://my-api.com/api/gettopics?text=${tweet.clean_text}`;

      const [sentiment, keywords, topic] = await Promise.all([
        await fetch(url1).then((res) => res.json()),
        await fetch(url2).then((res) => res.json()),
        await fetch(url3).then((res) => res.json()),
      ]);

      tweet['sentiment'] = sentiment.sentiment;
      tweet['keywords'] = keywords.keywords;
      tweet['topic'] = topic.topics;

      newData.push(tweet);
    }

    return newData;
};

const handleSubmit = async () => {
    ...
    setLoading(true);

    const data = await getData(); // this is the raw data plus cleaned text

    const newData = await getOtherData(data);
    setData(newData);

    setLoading(false);
};

I could probably setData after getting the data without the text analysis, but I'm not sure how to later update the other components with the new analyzed data. Setting the data again would cause everything to update/reload, right? I thought of separating the state for each; one with the raw data (and cleaned text) only, and another with the raw data + text analysis results. Does that make sense or is there a better way?

You can use useEffect() . Eg:

(This example is to be understood as an illustration of the principle. This is very likely not the optimal approach, or might even not work at all for your use case. Eg it would fail if data can change independent of these loading calls, or eg the loading state might be unnecessary if data can never change outside of these loading calls. You also might want to consider to use two useEffect() blocks and custom hooks )

useEffect( () => {

  if( !data && !loading ){   // <-- some condition to check if data is loaded
     setLoading( true );
     getData().then( firstData => {
       setLoading( false );
       setData( firstData );
     });

  } else if( !data.containsNewData && !loading ) { // <-- some condition to check for new data
     setLoading( true );
     getOtherData( data ).then( newData => {
       setLoading( false );
       setData( newData );
     });

  } else {
     // all data is available
  }
}, [ data ]);

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