Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

487
Visualizações
React useEffect and React-Query useQuery issue?

I'm still new to React so forgive me if this is a silly approach to this problem.

My goal: Global error handling using a context provider and a custom hook.

The Problem: I can't remove errors without them immediately being re-added.

I display my errors via this component in the shell...

import React, { useState, useEffect } from 'react'
import Alert from '@mui/material/Alert'
import Collapse from '@mui/material/Collapse'
import { useAlertContext } from '@/context/alert-context/alert-context'

export default function AppAlert () {
  const [show, setShow] = useState(false)
  const alertContext = useAlertContext()

  const handleClose = () => {
    alertContext.remove()
    setShow(false)
  }

  useEffect(() => {
    if (alertContext.alert) {
      setShow(true)
    }
  }, [alertContext.alert])

  return (
    <Collapse in={show}>
      <Alert severity='error' onClose={handleClose}>
        {alertContext.alert}
      </Alert>
    </Collapse>
  )
}

I have a provider setup that also exposes a custom hook...

import React, { useState, createContext, useContext } from 'react'

const AlertContext = createContext()

const AlertProvider = ({ children }) => {
  const [alert, setAlert] = useState(null)

  const removeAlert = () => setAlert(null)

  const addAlert = (message) => setAlert(message)

  return (
    <AlertContext.Provider value={{
      alert,
      add: addAlert,
      remove: removeAlert
    }}
    >
      {children}
    </AlertContext.Provider>
  )
}

const useAlertContext = () => {
  return useContext(AlertContext)
}

export {
  AlertProvider as default,
  useAlertContext
}

And finally I have a hook setup to hit an API and call throw errors if it any occur while fetching the data. I'm purposely triggering a 404 by passing a bad API path.

import { useEffect } from 'react'
import { useQuery } from 'react-query'
import ApiV4 from '@/services/api/v4/base'
import { useAlertContext } from '@/context/alert-context/alert-context'

export const useAccess = () => {
  const alertContext = useAlertContext()
  const route = '/accessx'
  const query = useQuery(route, async () => await ApiV4.get(route), {
    retry: 0
  })

  useEffect(() => {
    if (query.isError) {
      alertContext.add(query.error.toString())
    }
  }, [alertContext, query.isError, query.error])

  return query
}

This code seems to be the issue. Because alertContext.remove() triggers useEffect here and query.error still exists, it immediately re-adds the error to the page on remove. Removing alertContext from the array works, but it is not a real fix and linter yells.

  useEffect(() => {
    if (query.isError) {
      alertContext.add(query.error.toString())
    }
  }, [alertContext, query.isError, query.error])
about 4 years ago · Juan Pablo Isaza
3 Respostas
Responde à pergunta

0

This is a perfectly fine approach to the problem. You've also accurately identified the problem. The solution is to create a second hook with access to the methods that will modify the context. AppAlert needs access to the data in the context, and needs to update when AlertContext.alert changes. UseAccess only needs to be able to call AlertContext.add, and that method wont change and trigger a re-render. This can be done with a second Context. You can just expose one Provider and bake the actions provider into the outer context provider.

import React, { useState, createContext, useContext } from 'react'

const AlertContext = createContext()
const AlertContextActions = createContext()

const AlertProvider = ({ children }) => {
  const [alert, setAlert] = useState(null)

  const removeAlert = () => setAlert(null)

  const addAlert = (message) => setAlert(message)

  return (
    <AlertContext.Provider value={{ alert }}>
      <AlertContextActions.Provider value={{ addAlert, removeAlert }}>
        {children}
      </AlertContextActions.Provider>
    </AlertContext.Provider>
  )
}

const useAlertContext = () => {
  return useContext(AlertContext)
}

export {
  AlertProvider as default,
  useAlertContext
}

Now, where you need access to the alert you use one hook and where you need access to the actions you use the other.

// in AppAlert

import { useAlertContext, useAlertContextActions } from '@/context/alert-context/alert-context'

...

const { alert } = useAlertContext()
const { removeAlert } = useAlertContextActions()

And finally

// in useAccess
import { useAlertContextActions } from '@/context/alert-context/alert-context'

...

const { addAlert } = useAlertContextActions()
about 4 years ago · Juan Pablo Isaza Relatório

0

So I found a solution that seems to work for my purposes. I got a hint from this article. https://mortenbarklund.com/blog/react-architecture-provider-pattern/

Note the use of useCallback above. It ensures minimal re-renders of components using this context, as the function is guaranteed to be stable (as its memoized without dependencies).

So with this I tried the following and it solved the problem.

import React, { useState, createContext, useContext, useCallback } from 'react'

const AlertContext = createContext()

const AlertProvider = ({ children }) => {
  const [alert, setAlert] = useState(null)

  const removeAlert = useCallback(() => setAlert(null), [])

  const addAlert = useCallback((message) => setAlert(message), [])

  return (
    <AlertContext.Provider value={{
      alert,
      add: addAlert,
      remove: removeAlert
    }}
    >
      {children}
    </AlertContext.Provider>
  )
}

const useAlertContext = () => {
  return useContext(AlertContext)
}

export {
  AlertProvider as default,
  useAlertContext
}
about 4 years ago · Juan Pablo Isaza Relatório

0

My goal: Global error handling

One problem with the above useEffect approach is that every invocation of useAccess will run their own effects. So if you have useAccess twice on the page, and it fails, you will get two alerts, so it's not really "global".

I would encourage you to look into the global callbacks on the QueryCache in react-query. They are made for this exact use-case: To globally handle errors. Note that to use context, you would need to create the queryClient inside the Application, and make it "stable" with either useRef or useState:

function App() {
  const alertContext = useAlertContext()
  const [queryClient] = React.useState(() => new QueryClient({
    queryCache: new QueryCache({
      onError: (error) =>
        alertContext.add(error.toString())
    }),
  }))

  return (
     <QueryClientProvider client={queryClient}>
       <RestOfMyApp />
     </QueryClientProvider>
  )
}

I also have some examples in my blog.

about 4 years ago · Juan Pablo Isaza Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda