Wątek przeniesiony 2023-09-15 11:42 z JavaScript przez Riddle.

Błąd "Uncaught Error: could not find react-redux context value; please ensure the component is wrapped in a <Provider>"

0

Jestem w trakcie przepisywania istniejącej apki na Tanstack Query. Właściwie wszystko hulało do momentu kiedy chciałem zastosować centralną obsługę błędów. Struktura plików (trochę to długie, ale chcę rozwiać wszystkie możliwe wątpliwości) jest taka:

główny index

ReactDOM.render(
  <AppProvider>
    <App />
  </AppProvider>,

  document.getElementById("root")
);

App.tsx

const App = () => {
  
  return (
    <main>
      <Routes>
        <Route path={ROUTES.LANDING} element={<LandingPage />} />
        <Route path={ROUTES.SEARCH} element={<SearchPage />} />
        {location?.state?.results && (
          <Route path={"/" + location.state.results} element={<WeatherInformationsPage />} />
        )}
        <Route path={ROUTES.NOPAGE} element={<NoPage />} />
      </Routes>
    </main>
  );
};

AppProvider.tsx

const AppProvider: FC = ({ children }) => {
  // const queryClient = useQueryClient();
  return (
    <Provider store={store}>
      <QueryClientProvider client={useQueryClient()}>
        <StyledEngineProvider injectFirst>
          <ThemeProvider theme={theme}>
            <SnackbarProvider
              maxSnack={3}
              anchorOrigin={{
                vertical: "bottom",
                horizontal: "center",
              }}
            >
              <PlaceContextProvider>
                <CheckSupportForLocalStorage>
                  <CheckSupportForGeolocation>
                    <SetBackground>
                      <Router>{children}</Router>
                    </SetBackground>
                  </CheckSupportForGeolocation>
                </CheckSupportForLocalStorage>
              </PlaceContextProvider>
            </SnackbarProvider>
          </ThemeProvider>
        </StyledEngineProvider>
      </QueryClientProvider>
    </Provider>
  );
};

No i na końcu ten najważniejszy useQueryClient(). Uwaga, to jest postać docelowa, oczekiwana, ale nie działająca - w tej postaci on nie działa o czym poniżej

const useQueryClient = () => {
  const { showErrorMessage } = useDispatchAction();
  const queryErrorHandler = (err: unknown): void => {
    const axiosError = err as AxiosError;
    showErrorMessage(axiosError)
  };

  const defaultQueryClientOptions = {
    queries: {
      onError: queryErrorHandler,
      retry: false,
      cacheTime: 1.1 * (60 * 1000),
      refetchOnMount: false,
      refetchOnWindowFocus: false,
      staleTime: 1 * (60 * 1000),
      refetchInterval: 1 * (60 * 1000),
    },
  };

  return new QueryClient({
    defaultOptions: defaultQueryClientOptions,
  });
};

Błąd w konsoli pokazuje się kiedy w powyższym umieszczam linię

const { showErrorMessage } = useDispatchAction();

I biorąc pod uwagę strukturę AppProvidera nie rozumiem dlaczego - przecież jest to objęte Providerem (przedtem próbowałem trochę inaczej i wtedy faktycznie nie było).

1

Wydaje mi się, że hook useQueryClient powinien zostać wywołany z innego komponentu niż AppProvider, ponieważ on sam nie ma dostępu do wartości providerów, które renderuje.

0
Xarviel napisał(a):

Wydaje mi się, że hook useQueryClient powinien zostać wywołany z innego komponentu niż AppProvider, ponieważ on sam nie ma dostępu do wartości providerów, które renderuje.

Pytanie, z jakiego? Jeżeli przyczepię go do App, będzie na pewno poza Providerem

0

Zawsze możesz rozbić AppProvider na mniejsze części

const AppProvider = ({ children }) => {
  return (
    <Provider>
      {/* tutaj wszystkie providery, które nie muszą korzystać z useQueryClient, lub jego Providera */}
    </Provider>
  );
}

const AppProviderWithUseQueryClient = ({ children }) => {
  const queryClient = useQueryClient();

  return (
    <QueryClientProvider client={queryClient}>
      {/* tutaj wszystkie providery, które potrzebują useQueryClient, lub jego Providera do działania */}   
    </QueryClientProvider>
  )
}

const App = () => {
  /* tutaj bez zmian */
}

ReactDOM.render(
  <AppProvider>
    <AppProviderWithUseQueryClient>
      <App />
    </AppProviderWithUseQueryClient>
  </AppProvider>,

  document.getElementById("root")
);

Najważniejsze jest to, aby główny AppProvider zwracał komponent odpowiedzialny za useDispatchAction, żeby można było bez problemu skorzystać z useQueryClient w innym komponencie.

0

Tyle, że IMO niezupełnie w tym jest problem. Błąd woła o konkretny kontekst to jest react- redux context. A struktura jest taka, że ten właśnie kontekts jest najbardziej zewnętrzny

<Provider store={store}>

Natomiast QueryClinetProvider jest dokładnie o jeden poziom niżej

<Provider store={store}>
  <QueryClientProvider client={useQueryClient()}>

I dlatego IMO useQueryClient() powinien widzieć kontekst Reduxa a tak jakby go nie widział, co jest dla mnie niespodzianką. Pytanie, czy QueryClientProvider można traktować jako "normalny" komponent, który powinien widzieć kontekst?

1

Podtrzymuje zdanie, że IMO jest w tym problem, ponieważ tak działa context w Reactcie. Trzeba rozbić to na 2 komponenty, albo wywołać hook w QueryClientProvider jeśli masz do niego dostęp.

0

Ok, przetestuję

Działa jak złoto, dziękuję :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1