问题
I'm rewriting a Ionic project to work with ngrx because it is growing a lot and I need to maintain a centralized state.
I have implemented it with ngrx and using normalized states with the help of Normalizr.
Now I have a doubt on how to pass to dumb component a populated object:
Suppose I have two interfaces:
interface Conversation {
date: Date,
lastMessage: string //this is the id of a message entity coming from normalizr
}
and
interface Message {
content: string
}
Now when I'm trying to get all conversations to pass to the dumb component I'm using a selector like this:
getConversations() {
//select all the conversations to have a shape of Conversation[]
let conversations$ = this.store.select(state => Object.keys(state.entities.conversations).map( (id:string) => state.entities.conversations[id] ));
//select all the messages in a shape { [id:string] : Message }
let messages$ = this.store.select(state => state.entities.messages);
return Observable.combineLatest(
conversations$,
messages$,
(conversations, messages) => {
return conversations.map(conversation => {
return { ...conversation, lastMessage: messages[conversation.lastMessage] }
});
}
)
}
But the Observable I'm returning is not an array of Conversation[] because
return { ...conversation, lastMessage: messages[conversation.lastMessage] }
is putting inside 'lastMessage' an object of type Message instead of a String.
I tried to use interfaces with the object type instead of strings
interface Conversation {
date: Date,
lastMessage: Message
}
But then I cannot use selector like
this.store.select(state => state.entities.messages[conversation.lastMessage]
because it is not a string anymore.
How can I achieve this?
Thank you
回答1:
The short answer to this question would be the following:
You need to select the content
property from the last message, as in:
return { ...conversation, lastMessage: messages[conversation.lastMessage].content }
The longer answer:
The same can be achieved with selectors, this has the benefit that it's more re-usable in comparison with the RxJS operators.
A selector is a pure function that takes the state as an argument and returns a slice of the store’s state. Instead of just returning the data stored in the store, a selector can compute derived data based on the available data. This allows you to only store the basics, keeping the state tree as compact as possible. Another important part is that selectors could be composed, that is: a selector can take one or multiple other selectors as its input.
For example:
// getters
export const selectUser = (state: AppState) => state.selectedUser;
export const selectAllBooks = (state: AppState) => state.allBooks;
// create a selector with `createSelector` and use the getters above
export const selectVisibleBooks = createSelector(
selectUser,
selectAllBooks,
(selectedUser: User, allBooks: Books[]) => {
if (selectedUser && allBooks) {
return allBooks.filter((book: Book) => book.userId === selectedUser.id);
} else {
return allBooks;
}
}
);
In your component to select select the books, you can do:
this.books = this.store.pipe(select(selectVisibleBooks));
More info:
- docs
- Sharing data between modules is peanuts - A dive into NgRx selectors
来源:https://stackoverflow.com/questions/53791561/ngrx-normalized-state-selector