I am trying to move to RTK Query, but am struggling with one thing - how to access React Context.

I currently am using custom hooks to query a back end. Depending on the environment (dev, uat, prod), there will be different backend servers I need to call. I store these backend server addresses in context and can access it from my custom hooks. But if I move to RTK Query, I cannot get hold of the BackendServer Context.

How can I access context from the createApi() method of RTK Query? Or should I be storing these server addresses somewhere other than context?

src/BackendContext.ts

export const BackendServers {
  Pokemon: string,
  Marvel: string
}

const DEV: BackendServers = {
  Pokemon: 'http://pokeapi-dev.co/',
  Marvel: 'http://marvelapi-dev.com/'
}

const UAT: BackendServers = {
  Pokemon: 'http://pokeapi-uat.co/',
  Marvel: 'http://marvelapi-uat.com/'
}

export const BackendServersContext = createContext<BackendServers>(DEV);

src/main.tsx

<BackendSErversContext.Provider value={DEV}>
 <App/>
</BackendServersContext>

src/hooks/useCustomHook.ts

export const useCustomHook = () => {
  const backendServers = useContext(BackendServersContext);

  console.log('This hook will be calling ' + backendServers.Pokemon);
  ....
}

How can I access context from the RTK Query createApi() function?

src/services/pokemon.ts

// Or from '@reduxjs/toolkit/query/react'
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query'

export const pokemonApi = createApi({
  baseQuery: fetchBaseQuery({ 
    baseUrl: 'https://pokeapi.co/api/v2/'    // !! How can I get this server name from context? !!
  }),
  endpoints: (build) => ({
    getPokemonByName: build.query({
      query: (name: string) => `pokemon/${name}`,
    }),
  }),
})

4 Replies 4

This is potential XY problem. Should base url really be a context? A context makes sense if a value relies on the hierarchy of components, including root. This allows each application instance to have a specific value, e.g. testability and SSR. Since only 1 url is used at once, there's no such requirement. You won't have this problem if you define base url as env variable (or global variable, if you need more flexibility), this is how it's usually done.

Thank you Estus you make a fair point. In this application it is possible for the user to change the BackendContext value, and for example switch betwen looking at dev and production. Yes, I could set it as a global variable, but that does not feel to me the "React way".

"switch betwen looking at dev and production" is not common at runtime. If this is the intention, a common way is to manage backend urls in env vars and run a dev server with dev and prod environment respectively. They can run side by side at different ports if necessary. Doing this dynamically with global variable would require to reinitialize pokemonApi with new baseUrl value, which would be awkward. At this point this still looks like XY problem to me.

Apart from the env case, changing some config option is not uncommon and this is usually implemented by letting baseUrl be a getter function, however, it can only be a string, so this is not provided. The closest opportunity that wouldn't require to create wrappers around RTK functions is to use custom fetch implementation that will prepend urls with base part instead of baseUrl.

You could convert the context to a state slice and store all the backend server configs in the store and access whichever you need via a custom base query function (see https://redux-toolkit.js.org/rtk-query/usage/customizing-queries#constructing-a-dynamic-base-url-using-redux-state) or update your API slice creators to be factory functions that consume a specific/selected backend server config and return the current API slice with the new base URL baked-in.

I agree with Estus here though... it's a bit odd that you would have a need to switch between environments once the app is running. Is this just for development purposes? Save a bit of time from killing and restarting the development server with the new environment configuration?

Your Reply

By clicking “Post Your Reply”, 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.