ngrx normalized state selector

你。 提交于 2020-01-06 04:56:06

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!