-
-
Save markerikson/ad319fd7b04bd4eecdcfe7bf51dce7b1 to your computer and use it in GitHub Desktop.
| // 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"}, | |
| } | |
| } | |
| } | |
| } | |
| } | |
| */ |
So I decided that I'd try my hand with nesting slices. This way I can keep all my entities reducers/thunks/selectors etc under their own slice, yet keep a top level "entities" slice. But i'm stumped. I can dispatch the actions, and see them working. I just can't workout how to get the slice reducers to combine in the parent slice. https://stackblitz.com/~/github.com/rickysullivan-gallagher/vitejs-vite-ctk89t
@rickysullivan-gallagher I'm not actually sure what you're trying to do there, and this gist isn't a great place to provide support :) Could you drop by the #redux channel in the Reactiflux Discord ( https://www.reactiflux.com ) and ask there?
Will do @markerikson. I wasn't feeling confident this was the right place.
I am on "@reduxjs/toolkit": "^2.6.0" and was getting the following error from the typings on the messages array:
Generic type 'EntityState<T, Id>' requires 2 type argument(s).
Explicitly typing the EntityState with a string type for the Id resolved the error:
interface ChatRoomEntry {
id: string;
messages: EntityState<Message, string>;
}@Patrick-Erichsen yeah, we updated the EntityState type in RTK 2.0.
Thanks for the reply, @markerikson. In the words of the great 90s punk rock band, The Offspring, I'm going to Keep 'Em Separated. I like the idea of knowing that each entity has its own home and along with it, auto-generated selectors to boot. Thanks for all your awesome work.