changing state with RTK Query
Asked Answered
F

1

12

I'm learning about RTK Query and really confused. I'd be happy if someone could point me towards the right direction. My question is how one can manipulate the state of the application store the same way as it is done when using createAsyncThunk and setting up extraReducers.

export const asyncApiCall = createAsyncThunk("api/get_data", async (data) => {
    
    const config = {
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        }
   
    };
    
    const res = await axios.get( "http://apiserver/path_to_api",data,config );
    return res['data']
} )

export const mySlice  = createSlice({
    name:"mySliceName",
    initialState:{
        data: [],
        loadInProgress: false,
        loadError: null,
        extraData: {
           // something derived based on data received from the api 
        }

    },

    extraReducers: {
        [asyncApiCall .pending]: (state) => {
            state.loadInProgress = true;
    
        },
        [asyncApiCall .fulfilled]: (state,action) => {
            
            state.loadInProgress = false;
            state.data = action.payload;

            state.extraData = someUtilFunc(state.data)
           
        },
        [asyncApiCall.rejected]: (state) => {
            state.loadInProgress = false;
            state.loadError= true;
        },
       
    }
 })

Now I'm replacing it with RTK Query. My current understanding is that RTK Query automatically generates hooks for exposing data received from the api and all the query-related info like if it's pending, if an error occurred etc.

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


export const apiSlice = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  endpoints: builder => ({
    getData: builder.query({
      query: () => '/get_data'
    }),

    setData: builder.mutation({
        query: info => ({
          url: '/set_data',
          method: 'POST',
          body: info 
        })
      })

  })
})

export const { useSendDataMutation, useGetDataQuery } = apiSlice 

If I want to store some additional data that may be affected by the api calls should I create another slice that will somehow interact with the apiSlice, or is it possible to incorporate everything in this existing code? I'm sorry for possible naivety of this question.

Forster answered 8/8, 2022 at 15:58 Comment(0)
M
16

The short answer is that RTK Query is focused on purely caching data fetched from the server. So, by default, it stores exactly what came back in an API call response, and that's it.

There are caveats to this: you can use transformResponse to modify the data that came back and rearrange it before the data gets stored in the cache slice, and you can use updateQueryData to manually modify the cached data from other parts of the app.

The other thing to note is that RTK Query is built on top of standard Redux patterns: thunks and dispatched actions. Every time an API call returns, a fulfilled action gets dispatched containing the data. That means you can also apply another suggested Redux pattern: listening for that action in other reducers and updating more than one slice of state in response to the same action.

So, you've got three main options here:

  • If the "extra data" is derived solely from the server response values, you could use transformResponse and return something like {originalData, derivedData}
  • You could just keep the original data in the cache as usual, but use memoized selector functions to derive the extra values as needed
  • If you might need to update the extra values, then it's probably worth looking at listening to a query fulfilled action in another slice and doing something with it, like this silly example:
import { api } from "./api";

const someExtraDataSlice = createSlice({
  name: "extraData",
  initialState,
  reducers: {/* some reducers here maybe? */},
  extraReducers: (builder) => {
    builder.addMatcher(api.endpoints.getPokemon.matchFulfilled, (state, action) => {
      // pretend this field and this payload data exist for sake of example
      state.lastPokemonReceived = action.payload.name;
    }
  }
})
Madrigalist answered 9/8, 2022 at 1:12 Comment(4)
This answer works just on the request that is not cached, there's a workaround to call it every time? You can replicate the issue if you do the following(taking the getPokemon as example). Let's say we have a list of Pokemon, and we call this getPokemon endpoint every time an item is clicked, so now if you do the following steps: 1-Click on first pokemon - it works 2-Click on second Pokemon - it works 3-Clicking again on the first Pokemon - doesn't work, as the request is already cached it doesn't enter on matchFulfilled matcher anymoreKarlkarla
@Madrigalist hi, why are we always pushing RTK Query first? If I want to fetch data to work on it in my app and then push it back to the server maybe old Redux-thunk makes more sense? Sometimes we don't care about caching and errors/pending status is not a big cost for putting nice logic away of component in one place and not spread it across rtk query and extra reducers copying data from cache do other slice const loadPokemon = name => async dispatch => { const pokemon = await fetchPokemon(); dispatch(setCurrentPokemon) }Trygve
using above give me option to work wih my pokemon in my slice functions and build whole logic there and if it is ready write similar thunk for pushing changes. It is killing me. @Madrigalist pls help me to understand why it is worst approachTrygve
@Trygve your code there will run, but it's also completely ignoring anything that might go wrong with the request, or anything the UI might want to do to track status. Additionally, RTK Query already handles the details of how to fetch the data - you just supply a URL, and you get the caching and status trackin_, and a thunk, and a query hook, all "for free". Ultimately the point of RTK Query is to do all the work that you used to have to write lots of code for, but do it for you automatically.Madrigalist

© 2022 - 2024 — McMap. All rights reserved.