3

We are in the process of integrating RTK query in our app.

Our current "architecture" is as follow:

All the business logic actions are written inside services which are plain JS classes.
Those services are passed using react context in order for the component tree to be able to call services functions.

As of now those services were accessing the redux store directly to perform the appropriate logic.

Now that we are moving to RTK, accessing the RTK cache from a service is less trivial:
As far as I can see, the only way to access it is via the select function of the relevant endpoint.

The point is that this method is a "selector factory" and using it outside of a react component doesn't seems to be the right way to go.

Here is an exemple:

class TodoService {
   getTodoTitle( todoId: string ) {
     // This doesn't looks the right way to do it
     const todoSelector = api.endpoints.getTodo.select( {id: todoId } );
     const todo = todoSelector( state )
     return todo.data.title
   }
}

Is there any way to implement safely the following code

class TodoService {
   getTodoTitle( todoId: string ) {
      // Is there any way to do that kind of call ?
      const todoEntry = api.endpoints.getTodo.getCacheEntry( {id: todoId } );
      return todoEntry.data.title
   }
}

I guess that the answer is "no" and I have to refactor our whole architecture, but before doing so I'd like to be sure that there is no alternate approach.

Note that I could build the cache entry key by myself, but that also doesn't sound like a robust approach...

Clement
  • 3,860
  • 4
  • 24
  • 36

1 Answers1

1

The thing is that you don't want to just get the cache entry - the selector does a little more for you than just that. So let's just stay with "please use the selector" and "we won't add another way of doing that" because, selectors is how your code should interact with Redux all the time - React or not.

If you are not calling this code from React where you would need a stable object reference, it is good as it is. The selector factory will create a new selector and thus you get an un-memoized result. This is not perfect, but if you are not relying on referential equality, it also does not hurt at all.

If you want to have the referential equality, you'll have to store the selector in some way, probably as a class property.

Something like this would be possible:

class TodoService {
   getSelectorForTodo(todoId: string) {
     if (this._lastId !== todoId) 
       this._lastSelector = api.endpoints.getTodo.select( {id: todoId } )
     return this._lastSelector
   }

   getTodoTitle( todoId: string ) {
     const todo = this.getSelectorForTodo(todoId)(state)
     return todo.data.title
   }
}
phry
  • 35,762
  • 5
  • 67
  • 81
  • Please, can you explain where I can get the state variable outside of the component? Because typescript does not like my store.getState() method – Demian Oct 10 '22 at 10:41
  • @DemyanNetlyukh you would import your store and call `store.getState()`. – phry Oct 10 '22 at 11:51
  • I use RTK Query for data management. So, I probably need a variant similar to yours, but I can't understand where you get the `state` variable. I put the `store.getState()` but it just returns `undefined`. I really appreciate any help you can provide. – Demian Oct 10 '22 at 14:33
  • @DemyanNetlyukh `store.getState()` can never return `undefined`. If you get that returned, you are not calling `getState()` on a Redux store. At this point you should maybe open your own question though. – phry Oct 10 '22 at 15:09