I am working on the chat that is utilising RTK Query with entity adapters.
I currently have 2 different entity adapters; one for chat and one for messages.
How can I go about selecting all messages for a specific chat? Do I need to store array of message ids inside of the chat adapter or do I select all messages and filter them by parent_chat_uuid property?
What is more efficient way to approach this? Is there a built in selector that allows you to select entities by its property rather then the id? I imagine mapping over extremely large array to find object with certain property is pretty expensive.
Thanks
edit:
I am initially querying latest chats and once cacheDataLoaded I add many chats to entity adapter.
I also setup websocket connection and subscribe to 'message' event after data is loaded. Everytime message is notified by ws I addOne message. If status like 'delivered' or 'read' is notified I would call updateOne.
I am actually trying to rewrite project that was not-using RTK Query and entity adapters and messages were always added to the messages array inside of the chat object. Websocket connection and updates were handle by middleware that was calling async thunks -> updating store.
I am trying move all the middleware to be handle inside of the rtk queries / mutation logic and call actions from there directly (or thunks with more logic that call actions).
**off topic and I can create a separate post for this but is there any benefit in creating a App Thunk (not async) that just calls an action? Should I just call actions instead in this case?
I'm an RTK maintainer, and "author" of createEntityAdapter
(technically I ported it from NgRX, but I did a lot of work on it).
I actually did something like this myself on a previous project. I'd put a simplified version of the code into a gist, but I'll post it here for posterity too.
The technique that I used was nested entity adapters: a top-level one that stores chat rooms, and a nested one inside each room entry that stores that room's messages:
// Example of using multiple / nested `createEntityAdapter` calls within a single Redux Toolkit slice
interface Message {
id: string;
roomId: string;
text: string;
timestamp: string;
username: string;
}
interface ChatRoomEntry {
id: string;
messages: EntityState<Message>;
}
const roomsAdapter = createEntityAdapter<ChatRoomEntry>();
const messagesAdapter = createEntityAdapter<Message>();
const fetchRooms = createAsyncThunk(
"chats/fetchRooms",
chatsAPI.fetchRooms
);
const fetchMessages = createAsyncThunk(
"chats/fetchMessages",
async (roomId) => {
return chatsAPI.fetchMessages(roomId);
}
)
const chatSlice = createSlice({
name: "chats",
initialState: roomsAdapter.getInitialState(),
reducers: {
},
extraReducers: builder => {
builder.addCase(fetchRooms.fulfilled, (state, action) => {
const roomEntries = action.payload.map(room => {
return {id: room.id, messages: messagesAdapter.getInitialState()};
});
roomsAdapter.setAll(state, roomEntries);
})
.addCase(fetchMessages.fulfilled, (state, action) => {
const roomId = action.meta.arg;
const roomEntry = state.entities[roomId];
if (roomEntry) {
messagesAdapter.setAll(roomEntry.messages, action.payload);
}
})
}
})
/*
Resulting state:
{
ids: ["chatRoom1"],
entities: {
chatRoom1: {
id: "chatRoom1",
messages: {
ids: ["message1", "message2"],
entities: {
message1: {id: "message1", text: "hello"},
message2: {id: "message2", text: "yo"},
}
}
}
}
}
*/
That said, your situation sounds somewhat different, because you specifically said you're using RTK Query and trying to stream something. Because of that, I'm actually not sure how you're trying to set up the endpoints and store the data atm. If you can edit your post with more details and then leave a comment, I can try to update this response to give more advice.
Per the last question specifically: no, there's no built-in selectors for searching by properties, and doing that does generally turn into "re-filter all the items any time there's an update".