Xiaoye Yang
Xiaoye Yang

Reputation: 73

api data fetching best practice in react

scenario:

I have two api calls, and they both contribute to the same state, so what I did originally was just await two api calls in the same useEffect. However, one of them is a fairly slow one, so I need to wait extra long time for the page rendering.

const [loading, setLoading] = useState(true)

  useEffect(async() => {
            try{
               slow_api_call_promise = await slow_api_call
               quick_api_call_promise = await quick_api_call
               setLoading(false)
               let newState = quick_api_call_promise.data.merge(slow_api_call_promise.data)
               setState(newState)
               
            }catch(e){
              setLoading(false) 
              //show error message
            }
            
        },[])
    
    return <React.Fragment>
       {loading ? <SpinnerComponent />: <Actual Page />}

    </React.Fragment>

fortunately, the quick one actually provides most of the state I need for initial rendering, and the slow one contributes to just part of the page state. So for rendering experience, I separated them into two useEffect and set different loading state for them. It seems to work. but this looks silly to me, it is going to render twice. is there a better way, an optimized way to approach this.

const [loadingWhole, setLoadingWhole] = useState(true)
const [loadingPart, setLoadingPart] = useState(true)

useEffect(async() => {
        quick_api_call_promise = await quick_api_call
        setLoadingWhole(false)
    },[])

    useEffect(async() => {
        slow_api_call_promise = await slow_api_call
        setLoadingPart(false)
    },[])

Upvotes: 0

Views: 2751

Answers (1)

Bergi
Bergi

Reputation: 664630

Yes, you can keep a single effect, just do a first setState already after you've fetched the quick response:

const [state, setState] = useState(null);
const [loadingWhole, setLoadingWhole] = useState(true);
const [loadingPart, setLoadingPart] = useState(true);

async function fetchResults() {
    const quickResult = await callQuickApi();
    setState(quickResult.data);
    setLoadingPart(false);

    const slowResult = await callSlowApi();
    let newState = merge(quickResult.data, slowResult.data);
    setState(newState);
    setLoadingWhole(false);
}

useEffect(async() => {
    fetchResults().catch(err => {
        setLoadingPart(false);
        setLoadingWhole(false);
        //show error message
    });
},[]);

Btw, instead of 4 separate useState hooks you might want to consider using just one, which can be in 5 states only not a multitude of combinations (even !loadingWhole && loadingPart that doesn't make sense):

  • loading
  • error
  • partial response + loading
  • partial response + error
  • full response

Upvotes: 1

Related Questions