zaplec
zaplec

Reputation: 1819

How to access the data in store before query in Redux Toolkit Query?

I am trying to convert my Redux reducers, store etc using Thunks to use Redux-Toolkit (RTK) Query. Currently I have quite many thunks that are accessing the store to get some additional data before doing any queries to backend like this:

const someThunkCreator = (params: Params): AppThunk =>
  async (dispatch, getState) => {
    const someData = getState().some.nested.path.to.data
    const moreData = getState().another.nested.path.to.data

    // Probably do something with the data before querying

    const response = await fetch(`${url}/${someData.id}`, {
      method: 'PATCH',
      body: {
        ...moreData,
        ...params
      }
    })
  }

Now how should I convert that to RTK-Query endpoint? If I have multiple endpoints that are accessing some data in the store i.e. some user data, do I need to pass that data in as query parameter or is there some way to always inject that data into some specific queries? I guess with some enhancer I could inject that into every query, but then not all queries might need that data.

const myApi = createApi({
  reducerPath: 'myApi',
  baseQuery: fetchBaseQuery(),
  endpoints: builder => ({
    // Is there a way to do it this way?
    updateSomething1: builder.mutation({
      query: (something: TypeForSomething) => ({
        url: `${userManagementUrl}/${userId}`, // Here the userId would be got from the store based on whoever is the current user using the app
        method: 'PATCH',
        something
      })
    }),
    // Or do I need to do it this way and always somewhere in the code first get the data through some other endpoint query?
    updateSomething2: builder.mutation({
      query: (userId: string, something: TypeForSomething) => ({
        url: `${userManagementUrl}/${userId}`,
        method: 'PATCH',
        something
      })
    }),
  })
});

Upvotes: 2

Views: 2049

Answers (1)

Drew Reese
Drew Reese

Reputation: 203198

Instead of using the query function which only consumes a query argument

export type query = <QueryArg>(
  arg: QueryArg
) => string | Record<string, unknown>

use the queryFn which consumes the passed query argument in addition to the base query API, extra options, and base query function.

queryFn(
  arg: QueryArg,
  api: BaseQueryApi,
  extraOptions: BaseQueryExtraOptions<BaseQuery>,
  baseQuery: (arg: Parameters<BaseQuery>[0]) => ReturnType<BaseQuery>
): MaybePromise<
| {
    error: BaseQueryError<BaseQuery>
    data?: undefined
  }
| {
    error?: undefined
    data: ResultType
  }
>

The BaseQueryApi has the function to access the store's state:

export interface BaseQueryApi {
  signal: AbortSignal
  dispatch: ThunkDispatch<any, any, any>
  getState: () => unknown
}

Update the mutation endpoint to access the store from the api argument and access the value(s) you need from state.

Example:

updateSomething1: builder.mutation</* ResultType */, TypeForSomething>({
  queryFn: (something, api, extraOptions, baseQuery) => {
    const state = api.getState() as RootState;
    const userId = state.some.nested.path.to.data;
    // or const userId = selectUserId(state); if you've selector functions

    return baseQuery({
      url: `${userManagementUrl}/${userId}`,
      method: 'PATCH',
      something
    }) as { data: /* ResultType */ };
  },
}),

/* ResultType */ is what you'll need to declare and provide for what you are actually returning.

Upvotes: 2

Related Questions