0

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)
    },[])
8
  • Just don't await the slow api call... it'll still render twice but your code will be much less repetitive Commented Nov 11, 2021 at 19:19
  • 1
    "This looks silly to me, it is going to render twice" - but that's exactly what you wanted? Commented Nov 11, 2021 at 19:28
  • Notice you cannot pass an async function to useEffect anyway. So yes, just do two calls in one effect. Commented Nov 11, 2021 at 19:32
  • @MikeAbeln I have to choose this way, because the slower api call takes around 10 seconds, user won't tolerate a first paint time as 10 seconds Commented Nov 14, 2021 at 0:15
  • @Bergi yeah, I need both apis, and the slower one is really slow. so first api will return most of what i want, and for second api I put a loader on the stuff it is trying to load, so yeah logically, there will be two renders. But just wondering if there is any other more cleaner way, like hydrating, or combine these two useEffect into one or etc. Commented Nov 14, 2021 at 0:27

1 Answer 1

1

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
Sign up to request clarification or add additional context in comments.

4 Comments

will react batch the updates?
Depends on the react version iirc. It might batch the setState together with the setLoading, or it might not, that's why I suggested to use a single state. It will never batch the first update that happens before the await together with the second update that happens after the await.
Thank you so much! could you explain why it will never batch the first update with second update? all i know is react will do batch updates in lifecycle method or event handlers(where react handle things). Or could you point me to some documents or examples or source code?
Because after the first await, it's no longer react handling things, you've left the lifecycle method. It's the (implicit) promise handler that runs the code and makes the setState calls. (It might be more obvious if you try rewriting the code from async/await to .then() syntax).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.