kentcdodds / ama

Ask me anything!
https://github.com/kentcdodds/ama/issues?q=is%3Aissue+is%3Aclosed
685 stars 75 forks source link

How to handle sequenced ajax call with hook #618

Open shall-git opened 5 years ago

shall-git commented 5 years ago

Hi Kent, I need any suggestion on how to handle sequenced ajax call with custom hooks.

I made a rude custom useFetch hook that handle only simple GET requests that is something like this

function useFetch(url) {
    const [data, setData] = useState(null)
    const [loading, setLoading] = useState(true)
    const [error, setError] = useState(null)
    useEffect(() => {
        fetch(url)
            .then((data) => data.json())
            .then(data => {
                setData(data)
                setLoading(false)
            })
            .catch((err) => {
                setErr(err.toString())
                setLoading(false)
            })
    }, [url])

    return [loading, error, data]
}

I want to handle 2 consecutive fetch in wich the second one is based on the first response,

function Comp(props) {
    const [loading1, error1, data1] = useFetch("url1")
    const [loading2, error2, data2] = useFetch(`url2/${data1.id}`)

    return (
        ..something using data2
    )
}

The problem of course is that hooks are asyncronous, so the second useFetch runs before the first get finished, so i cannot use the data coming back in the second useFetch... So I ended up by splitting that in 2 nested components like so

function Comp1(props) {
    const [loading, error, data] = useFetch("url1")

    return data ? (
        <Comp2 dataid={data.id}/>
    ) : null
}

function Comp2(props) {
    const [loading, error, data] = useFetch(`url2/${props.dataId}`)

    return (
        ..something using data
    )
}

But it's seems weird, something like an hack... Any Idea?

Thank you

justsml commented 5 years ago

Hi @shall-git

It's maybe a bit unfamiliar, give it some time.

This is actually a really cool pattern, <Comp1> only renders the <Comp2> if props.dataId is set.

This is pretty good component encapsulation. However it could be more durable; the key here is avoiding tight-coupling. E.g. where relies on logic in to avoid errors.

Here's one way to make <Comp2> more durable against errors:

function Comp2(props) {
    // TIP: Here's how I'd implement a low-ceremony way to avoid rendering invalid state/props.
    if (!props.dataId) return <div>For details, please make a selection</div>

    const [loading, error, data] = useFetch(`url2/${props.dataId}`)

    return (
        ..something using data
    )
}

Btw, thanks @kentcdodds for all you do! 👍

kentcdodds commented 5 years ago

Thanks for the question @shall-git!

Note @justsml, your solution there breaks the rules of hooks by making useFetch conditionally called due to the if statement.

I'm actually ok with the "hack" mentioned by @shall-git, but I show a few other ways to solve the problem in today's DevTipsWithKent video: https://www.youtube.com/watch?v=y55rLsSNUiM&index=2&list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u&t=0s

Here's my solution: https://codesandbox.io/s/0owknqvzmn

Here's my Suspense solution: https://codesandbox.io/s/84jkm1p0wj (NOTE: DO NOT USE THE SUSPENSE SOLUTION AS IT IS SERIOUSLY FLAWED AND SUSPENSE IS NOT READY FOR DATA FETCHING, BUT IT IS SUPER COOL RIGHT!?)

I hope that's helpful!

justsml commented 5 years ago

@kentcdodds thanks for the clarification! Hooks are making me re-think components in such interesting ways.

Great video as always!

Is that reducer pattern you mentioned in the link? Or did i miss it in the video?

kentcdodds commented 5 years ago

Here's how I would write the reducer version of that: https://codesandbox.io/s/m58yn540j8

Note: there's more work to do there to cancel requests etc. But that's a good starting point.

BruceL33t commented 5 years ago

Great educative answer as always @kentcdodds . I'm just wondering if we can somehow justify beginning to use react-cache and suspense for data fetching in production today. What do we know about the public "api" that will for sure not change, and would it really be that risky to just start using https://github.com/facebook/react/tree/master/packages/react-cache and accepting the fact that minor changes to the api will most likely occur. I mean, last official warnings about using it was from mid february - does anyone have some inside info on whether the implementation is almost done or if it will be completely different in the end? We're in the process of rewriting a large app, and I really would like to handle cache, fetch and rendering of data as close as possible to the expected final solution. If using the react-cache is too risky right now, would something like https://github.com/CharlesStover/fetch-suspense make sense or do you see any problems with using it?

kentcdodds commented 5 years ago

I strongly recommend against trying to use suspense for data fetching before the react team says it's ready. Don't do it.

mpcen commented 4 years ago

Thanks for the question @shall-git!

Note @justsml, your solution there breaks the rules of hooks by making useFetch conditionally called due to the if statement.

I'm actually ok with the "hack" mentioned by @shall-git, but I show a few other ways to solve the problem in today's DevTipsWithKent video: https://www.youtube.com/watch?v=y55rLsSNUiM&index=2&list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u&t=0s

Here's my solution: https://codesandbox.io/s/0owknqvzmn

Here's my Suspense solution: https://codesandbox.io/s/84jkm1p0wj (NOTE: DO NOT USE THE SUSPENSE SOLUTION AS IT IS SERIOUSLY FLAWED AND SUSPENSE IS NOT READY FOR DATA FETCHING, BUT IT IS SUPER COOL RIGHT!?)

I hope that's helpful!

How would you test this?

kentcdodds commented 4 years ago

Good question @mpcen. I'll see about giving you an answer eventually :)

mpcen commented 4 years ago

Good question @mpcen. I'll see about giving you an answer eventually :)

Looking forward to it!