Shamoon
Shamoon

Reputation: 43531

What's the best practice with React hooks if I need to do some stateful orchestration?

I have 2 different components that need to do the same kind of orchestration:

    useEffect(() => {
        async function fetchData() {
            if (route.params['id']) {
                const resConversation = await axios(`...`)
                setConversationState({ ...conversationState, currentConversation: resConversation.data })

                const resParticipants = await axios(`...`)
                setParticipants(resParticipants.data)

                const resStream = await axios(`somestuff`)
                setAudioState({ ...audioState, ...resStream })
            }
        }

        fetchData()

    }, [])

The setAudioState and setConversationState are from useContext.

What's the best way for me to do this and keep my code DRY?

I thought about a custom useGetConversation hook, but I can only call that inside my component function body. Is there a better way?

Upvotes: 0

Views: 237

Answers (1)

Ori Drori
Ori Drori

Reputation: 191976

If you're calls are not dependant on each other, make parallel calls with Promise.all(), destructure the response, and then set them.

I would also avoid multiple set calls by using a reducer to set the state via useReducer.

useEffect(() => {
  if (!route.params['id']) {
    return
  }

  async function fetchData() {
    const [resConversation, resParticipants, resStream] = await Promise.a([
      axios(`...`),
      axios(`...`),
      axios(`somestuff`)
    ]);

    setConversationState(conversationState => ({ ...conversationState, currentConversation: resConversation.data }))

    setParticipants(resParticipants.data)

    setAudioState(audioState => ({ ...audioState, ...resStream }))
  }

  fetchData()
}, [])

If your calls are dependant on each other, make the calls first, and set the state later:

useEffect(() => {
  if (route.params['id']) {
    return
  }

  async function fetchData() {
    const resConversation = await axios(`...`)
    const resParticipants = await axios(`...`)
    const resStream = await axios(`somestuff`)
    setConversationState(conversationState => ({ ...conversationState, currentConversation: resConversation.data }))
    setParticipants(resParticipants.data)
    setAudioState(audioState => ({ ...audioState, ...resStream }))
  }

  fetchData()
}, [])

You can also bundle calling the API and setting state to a single function, but this seems a bit unreadable to me:

const callAndSet = async (axiosRequest, setter) => {
  const res = await axios(axiosRequest)

  setter(res)
}

useEffect(() => {
  if (!route.params['id']) {
    return
  }

  async function fetchData() {
    await callAndSet(`...`, resConversation => setConversationState(conversationState => ({ ...conversationState, currentConversation: resConversation.data })))

    await callAndSet(`...`, resParticipants => setParticipants(resParticipants.data))

    await callAndSet(`somestuff`, resStream => setAudioState(audioState => ({ ...audioState, ...resStream })))
  }

  fetchData()
}, [])

Upvotes: 1

Related Questions