I've written the following reducer to store the state items in my Angular 2 app. The Items are price offers for Financial Instruments (e.g. stocks/currencies).
My Reducer Implementation is as follows:
export const offersStore = (state = new Array<Offer>(), action:Action) => {
switch(action.type){
case "Insert":
return [
...state, action.payload
];
case "Update":
return state.map(offer => {
if(offer.Instrument === action.payload.Instrument)
{
return Object.assign({}, action.payload);
}
else return offer;
});
case "Delete":
return state.filter(offer => offer.Instrument !== action.payload )
default:
return state;
}
}
I managed to get Inserts, Updates and Deletes working - although it wasn't easy. I find Redux to be something of a paradigm shift away from how I've been coding for years.
I've got an Instrument Component/Page on my App - which shows all available information for one specific Instrument, indicated by InstrumentId e.g. "EUR/USD" (stored in the payload.Instrument property).
My problem is, I'm not sure how to efficiently search for a specific instrument and grab it out of the store. Not only this, but I also want the instrument I fetch to be updated if the Instrument in the store is updated as they are frequently via websocket push from the server. So I really need to search the store for a specific instrument, and return it as an Observable, that will continue to update the View Component based on new data that gets pushed to the store.
How can I achieve this?
What you're not getting is that for every action that is called on a reducer, the new state is returned.
From your example code in the question, your state is just a list of instruments.
There's no index, so the only way to check if an instrument is in the list is to search the whole list.
But what if your state was a dictionary? Furthermore, what if you kept a list of indexes seperate to the dictionary?
your state type is this:
Any time an action is executed, the new state is returned. It is an important concept in Redux, because state can never be mutated directly. You're actually best strictly enforcing this when you compose your reducer: (say you've got you "offers reducer" and another reducer, you combine them to one with compose:
Its easy to do things wrong in Redux - but using storeFreeze will throw up an error if you try to mutate the state directly. The point is that actions change state, and make a new state. They don't change the existing state - it lets us undo/redo... etc.
Using your example above I would use this as my Offer's reducer:
note that changes are made to a temporary state via object.assign (deep copy) and then the new state is returned.
The other answer to the question was a bit confusing. It went into the detail of how to combine different reducers, but it didn't make much sense to me.
in your reducers/index.ts you should have a type:
inside this index.ts, you should have functions that get the reducers:
inside our offerReducer and our otherReducer, we define functions that can query the data we need. These are anonymous functions, that are not linked to anything at present, but we will link them later (to the getReducerFunctions).
examples of these functions:
this does nothing. unless we apply it to some useful data (e.g. the offersRedeucer) that we made ealier, and we combine the two like this:
I've tried to anticipate the next problem you will face (a second reducer) as well as answering the question of "how do you get one item?" as I know you're going to stumble onto that issue pretty much immediately. But don't let that confuse you with your original query.... The trick to finding an item, is to maintaining a state which is a map of items (a dictionary) - never mutating that state and always returning a new state. Follow that guidance and your problem is solved. The next problem you will face is multiple reducers, but read again above and it should make sense.
I disagree with the previous answer about stackoverflow questions - they are whatever you need them to be. And if you need a tutorial, then they can be a tutorial. And I don't think that's a bad thing - particularly in niche subjects like RXJS where little advice and documentation is available.
Thanks for putting a bounty on this question - I hope that I have given you a better answer. Any further questions - just yell. I am happy to help. You will not get a "I'm reluctant to answer any more of your questions" from me... I am here to help - not to insult.
Okay I'll give it a shot at explaining a way to set this up and hopefully do it in a way you and others can understand.
So if you are storing a array of objects and need to get access to a row by a certain id or key then the way the ngrx example app shows you can do this is to create a object containing your object which will use (in the case of their book app) the book's id as the property key. You can set any string as a property name. So say you have your state with a property called "entities" whose value is a blank object. You can then take your array and create properties on the "entities" Object.
Lets start with just one row in the array of book objects. To add this row to the "entities" object you do it like this.
The above will make the book's id a property on the "entities" object.
If you were to inspect it in the console or debug tools it might look something like this after its creation.
The ngrx example app operates on the full array to add it to the state using the reduce operator on the javascript array.
And then create special functions to get parts of this state that can be composed together later to get the specific rows you need
And they compose these in the index.ts barrel in the example
This function will chain the calls together going from right to left. First calling getBooksState to get the reducer state and then calling fromBooks.getBooks and passing in the bookid's you want and the state from the previous "getBooksState" and allowing you to do the mapping to the selected states you need.
In your component you can import this function and use it to get just the books you want.
This returned observable is updated every time the store is updated.
Added: So the operator "let" on the store object passes in the current observable holding the store's state object to the function that is returned by the "getBooks" function.
Lets look closer at that.
Two functions are passed into the compose function here -- fromBooks.getBooks(bookIds) and getBooksState(). The compose function allows a value to be passed into the first function on the right and have that functions results passed into the function on the left and so on and so on. So in this case we are going to take the results of the function returned by "getBooksState" and pass it into the function returned by the function "fromBooks.getBooks(bookIds)" and then ultimately return that result.
These two functions take in a Observable. In the case of the function returned by getBooksState() it takes in as a parameter the store state observable object from which it will map/select (map and select are aliases for each other) to the slice of the store we care about and return the new mapped observable. That mapped observable will be passed into the function returned from the fromBooks.getBooks(bookIds) function. This function expects the state slice observable. This function does the new mapping by pulling out only the entities we care about. This new observable is what is ultimately returned by the "store.let" call.
If you need more clarification then please ask questions and I'll do my best to clarify.
https://github.com/ngrx/example-app