Search code examples
javascriptreactjsreduxredux-toolkitrtk-query

How to get an RTK Query API endpoint state (isLoading, error, etc) in a React Class component?


Ok, I think I've gone through almost all of RTK Query's docs, and read up on RTK Query's caching. Seems like it's a pretty big part of it, even if its not something I need at the moment.

So, I'm trying to do a simple query using RKT Query in a class-based component, and then select from the Redux Store the isLoading state for the endpoint call. However, currently in the render() {} of my LoginPage.jsx, the endpoint.<name>.select()(state) call on mapStateToProps within LoginPageContainer.jsx doesn't seem to be working. (See code below).

From looking at the examples from the docs on using RTK Query on classes, it looks like I'm missing a "cache key" in the .select(<cache_key>)(state) call. However, I haven't incorporated tags in my endpoint yet (I believe I don't have a need for them yet).

My question:

Can someone shed some light on what's the proper use on RTK Query generated endpoint's select() method used outside of React Hooks? I understand the idea behind cache tags for automatic re-fetching (but that's not likely what's wrong here), but I'm not sure how or what cache key I'm missing here to just get the running endpoint query state in a class component. Thanks, everyone!

The Code:

// LoginPage.jsx
import React, { Component } from 'react'
import PT from 'prop-types'
import LoginForm from './components/LoginForm'

export default class LoginPage extends Component {
  static propTypes = {
    loginWithUsername: PT.func.isRequired,
    loginWithUsernameState: PT.object.isRequired
  }

  render() {
    // This value never updates
    const { isLoading } = this.props.loginWithUsernameState
    // always outputs "{"status":"uninitialized","isUninitialized":true,"isLoading":false,"isSuccess":false,"isError":false}"
    // Even during and after running the `loginWithUsername` endpoint query
    console.log(this.props.loginWithUsernameState)
    return (
      <div>
        {isLoading && 'Loading ...'}
        <LoginForm
          onSubmit={(values) => this.props.loginWithUsername(values)} />
      </div>
    )
  }
}

// LoginPageContainer.jsx
import { connect } from 'react-redux'
import { teacherApi } from './api'
import LoginPage from './LoginPage'

const { loginWithUsername } = teacherApi.endpoints

const mapStateToProps = (state) => ({
  loginWithUsernameState: loginWithUsername.select()(state)
})
const mapDispatchToProps = (dispatch) => ({
  loginWithUsername: (payload) => dispatch(loginWithUsername.initiate(payload))
})

export default connect(mapStateToProps, mapDispatchToProps)(LoginPage)

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

export const teacherApi = createApi({
  reducerPath: 'teacherApi',
  baseQuery: fetchBaseQuery({ baseUrl: '/teacher/' }),
  endpoints: (builder) => ({
    loginWithUsername: builder.query({
      query: (data) => ({
        url: 'login',
        method: 'post',
        body: data,
        headers: { 'Content-Type': 'application/json' }
      }),
    }),
  }),
})

Solution

  • The "cache key" passed to endpoint.select() is the same variable you're passing to your hook:

    useGetSomeItemQuery("a");
    useGetSomeItemQuery("b");
    
    const selectSomeItemA = endpoint.select("a");
    const selectSomeItemB = endpoint.select("b");
    
    const itemAREsults = selectSomeItemA(state);
    const itemBResults = selectSomeItemB(state);
    

    This results in looking up state => state[apiSlice.reducerPath].queries["getSomeItem('a')"], or whatever the exact cached data field is for that item.