Keeping big requests in redux state - redux

I use immutable.js to keep my application state. It's made of multiple combined reducers, that are immutable maps like:
const initialState = new Map({
data: null,
page: 1,
...
});
So I have a request response I need to fetch, save somewhere, get data from, and update the state, whenever something on the webpage changes.
The question is - where do I keep raw request responses in redux?
Let's say, I want to keep it inside the state. I tried the following approaches:
case FETCH_RESPONSE:
return state.merge({
rawRequestResponse: action.response // very slow, deep conversion to Immutable
})
case FETCH_RESPONSE:
return state.set({
rawRequestResponse: action.response // also slow, it's converted to Immutable, though not deeply
})
case FETCH_RESPONSE:
return new Map({
rawRequestResponse: action.response, // just like above
...
})
But they all are too slow. What's the best way to do this?

Not sure about passing a plain object to set(), but if you pass the key as a string, then the value as the second argument, nothing should be converted:
return state.set('rawRequestResponse', action.response)

Related

update state in ngrx reducer immutablely

I'm becoming a bit confused about how to update state in NgRx reducer, here is the question.
Say I have a state
{
xxx: num
yyy: classtpe
...
data: Data[]
}
If I have an action to add a Data item to the list.
I know I can't call data.push() because that just update the array but data pointing to same array so in the reducer I have
state.data = cloneDeep(state.data)
state.data.push(newdata)
so state.data now is different from previous one because they are 2 individual arrays.
my question is, can I return state directly now? If yes then the old and new states point to same variable, they have exactally same members except data.
The other way is, return a brand new state like
return Object.assign({}, state, {
data: [...state.data, newdata]
})
or
const newstate = cloneDeep(state)
newstate.data.push(new data)
return newstate
this way new and old state are totally different.
I think it's actually related to how difference is checked in NgRx? first way they need to go through each memeber, if any member is different then the state is differnet?
2nd way the 2 states are different, but all the memebers need to be checked to see if contents are same.
I would recommend you to use ngrx-immer, https://github.com/timdeschryver/ngrx-immer
It's an Immer wrapper around ngrx reducers so you can update all of the state mutably, and it will do the rest for you. Immer is also used in the redux-toolkit to make it simple to update state.

Retrieve values from firebase database in conversation flow

I am trying to grab information from my firebase database after a particular intent is invoked in my conversation flow.
I am trying to make a function which takes a parameter of user ID, which will then get the highscore for that user, and then say that users highscore back to them.
app.intent('get-highscore', (conv) => {
var thisUsersHighestscore = fetchHighscoreByUserId(conv.user.id);
conv.ask('your highest score is ${thisUsersHighestScore}, say continue to keep playing.');
});
function fetchHighscoreByUserId(userId){
var highscoresRef = database.ref("highscores");
var thisUsersHighscore;
highscoresRef.on('value',function(snap){
var allHighscores= snap.val();
thisUsersHighscore = allHighscores.users.userId.highscore;
});
return thisUsersHighscore;
}
An example of the data in the database:
"highscores" : {
"users" : {
"1539261356999999924819020" : {
"highscore" : 2,
"nickname" : "default"
},
"15393362381293223232222738" : {
"highscore" : 78,
"nickname" : "quiz master"
},
"15393365724084067696560" : {
"highscore" : "32",
"nickname" : "cutie pie"
},
"45343453535534534353" : {
"highscore" : 1,
"nickname" : "friendly man"
}
}
}
It seems like it is never setting any value to thisUsersHighScore in my function.
You have a number of issues going on here - both with how you're using Firebase, how you're using Actions on Google, and how you're using Javascript. Some of these issues are just that you could be doing things better and more efficiently, while others are causing actual problems.
Accessing values in a structure in JavaScript
The first problem is that allHighscores.users.userId.highscore means "In an object named 'allHighscores', get the property named 'users', from the result of that, get the property named 'userId'". But there is no property named "userId" - there are just a bunch of properties named after a number.
You probably wanted something more like allHighscores.users[userId].highscore, which means "In an object named 'allHighscores', get the property named 'users', fromt he result of that, get the property named by the value of 'userId'".
But if this has thousands or hundreds of thousands of records, this will take up a lot of memory. And will take a lot of time to fetch from Firebase. Wouldn't it be better if you just fetched that one record directly from Firebase?
Two Firebase Issues
From above, you should probably just be fetching one record from Firebase, rather than the whole table and then searching for the one record you want. In firebase, this means you get a reference to the path of the data you want, and then request the value.
To specify the path you want, you might do something like
var userRef = database.ref("highscores/users").child(userId);
var userScoreRef = userRef.child( "highscore" );
(You can, of course, put these in one statement. I broke them up like this for clarity.)
Once you have the reference, however, you want to read the data that is at that reference. You have two issues here.
You're using the on() method, which fetches the value once, but then also sets up a callback to be called every time the score updates. You probably don't need the latter, so you can use the once() method to get the value once.
You have a callback function setup to get the value (which is good, since this is an async operation, and this is the traditional way to handle async operations in Javascript), but you're returning a value outside of that callback. So you're always returning an empty value.
These suggest that you need to make fetchHighScoreByUserId() an asynchronous function as well, and the way we have to do this now is to return a Promise. This Promise will then resolve to an actual value when the async function completes. Fortunately, the Firebase library can return a Promise, and we can get its value as part of the .then() clause in the response, so we can simplify things a lot. (I strongly suggest you read up on Promises in Javascript and how to use them.) It might look something like this:
return userScoreRef.once("value")
.then( function(scoreSnapshot){
var score = scoreSnapshot.val();
return score;
} );
Async functions and Actions on Google
In the Intent Handler, you have a similar problem as above. The call to fetchHighScoreByUserId() is async, so it doesn't finish running (or returning a value) by the time you call conv.ask() or return from the function. AoG needs to know to wait for an async call to finish. How can it do that? Promises again!
AoG Intent Handlers must return a Promise if there is an asyc call involved.
Since the modified fetchHighScoreByUserId() returns a Promise, we will leverage that. We'll also set our response in the .then() part of the Promise chain. It might look something like this:
app.intent('get-highscore', (conv) => {
return fetchHighscoreByUserId(conv.user.id)
.then( function(highScore){
conv.ask(`Your highest score is ${highScore}. Do you want to play again?`);
} );
});
Two asides here:
You need to use backticks "`" to define the string if you're trying to use ${highScore} like that.
The phrase "Say continue if you want to play again." is a very poor Voice User Interface. Better is directly asking if they want to play again.

How to know if Meteor Collection Subscription did not change. [Meteor + Blaze]

Below is the piece of subscription on UI.
Template.AssociateEmp.onCreated(function(){
this.validEmail = new ReactiveVar('');
const tpl = this;
this.autorun(() => {
var email = this.validEmail.get();
this.subscribe('GetUnassociatedUser', email, {
onReady: function () {},
onError: function () {}
});
});
});
Is there a way to know that even if the dynamic data changed (here validEmail), Meteor Subscription was unaffected and did not change its data on UI? Is there any flag or something that triggers when subscription data is unchanged?
Autorun, ReactiveVar and Subscriptions
In your code example the subscription itself will re-run the server's publication function as the subscription's input variable email depends on the reactive variable validEmail and thus triggers the autorun when validEmail changes.
You can easily check that on your server console by logging something to the console within the publication.
If validEmail remains unchanged than there is no reason for autorun to trigger (unless there are other reactive sources that may not be added to your code example).
What about the subscribed data
Now if something has caused the subscription to re-run and you want to know if the data of a collection has been changed you could easily check on collection.count() but this could be flawed.
Imagine your publication is parameterized to include different fields by different parameters, then the data that is transfered to the client side collection will be different.
You would then require a method to check on the client side collection's data integrity.
Use hashes to verify integrity
A possible help would be to generate hases from the dataset using the sha package.
You could for example create one hash of your whole collection:
// get data
const data = Collection.find().fetch();
// map data to strings
// and reduce to one string
const hashInput = data.map(doc => JSON.stringify(doc) ).reduce((a, b) => a + b);
// generate hash
const collectionHash = SHA256(hashInput);
After the next onReady you can generate a new hash of the collection and compare it with the previous hash. If they are different, then something has changed.
This also removes the need for iterating the collection's documents if you only want to know if the data has changed but it won't reveal which document has changed.
Hashing single documents
Hashing single documents gives you more insight about what has changed. To do that you only need to create a map of hashes of your collection:
// get data
const data = Collection.find().fetch();
// map data to strings
const hashes = data.map(doc => { _id: doc._id, hash: SHA256( JSON.stringify(doc) ) });
You can store these hashes together with a document's _id. If the hash of a document is different after a new subscription you can assume that the change is related to this document.
General notes
hashing is some kind of expensive operation so it might be difficult to keep up with performance on large collections
usually you should design your pub / sub and autorun in a way that when the input changes the output changes
code is cold-written so it may not work out of the box. Please let me know if something does not.

Meteor with Angular2 , Fetching all entries from a collection in single shot

I have successfully integeraed meteor with angular2 but while fetching the data from collection facing difficulties in getting at one shot, here is the steps:
Collection Name : OrderDetails
No Of records : 1000
Server:
Created publication file to subcribe the collection:
Meteor.publish('orderFilter', function() {
return OrderLineDetails.find({});
});
Client:
this.dateSubscription =
MeteorObservable.subscribe('orderFilter').subscribe(()=> {
let lines = OrderDetails.find({expectedShipDate:{$in:strArr}},{fields:
{"expectedShipDate":1,"loadNo":1},sort:{"expectedShipDate":1}}).fetch();
});
In this lines attribute fetches all the collection entries, but fails to subscribe for the changes
When I try with below one,
OrderDetails.find({expectedShipDate:{$in:strArr}},{fields:{"expectedShipDate":1,"loadNo":1},sort:{"expectedShipDate":1}}).zone().subscribe(results => {
// code to loop the results
});
In this am able to subscribe for the collection changes, but the results are looped for 1000 times , as 1000 entries in the colleciton.
Is there any way to get the whole collection entries in one single shot and mean time to subscribe the changes in the collection ?.
Yes, there are a couple of ways you can do it, mostly depending on how you want to handle the data.
If having everything at once is important, then use a Method such as:
MeteorObservable.call('getAllElements', (err, result) => {
// result.length === all elements
})
While on server side doing
Meteor.methods({
getAllElements:function(){return myCollection.find().fetch()}
})
Now, if you want to listen to changes, ofcourse you'll have to do a subscription, and if you want to lower the amount of subscriptions, use rxjs' debounceTime() function, such as (from your code):
this.theData.debounceTime(400).subscribe(value => ...., err =>)
This will wait a certain amount of time before subscribing to that collection.
Now, based on your intent: listening to changes and getting everything at once, you can combine both approaches, not the most efficient but can be effective.
As #Rager explained, observables are close to streams, so when you populate data on miniMongo (front end collection you use when you find() data and is populated when you subscribe to publications) it will start incrementing until the collection is in sync.
Since miniMongo is populated when you subscribe to a publication, and not when you query a cursor, you could either:
Try the debouceTime() approach
Use a Meteor.Method after subscribing to the publication, then sync both results, keeping the first response from the method as your starting point, and then using data from Collection.find().subscribe(collectionArray => ..., err=>) to do whatterver you want to do when changes apply (not that recommended, unless you have a specific use case for this)
Also, .zone() function is specific to force re-render on Angular's event cycle. I'd recomend not use it if you're processing the collections' data instead of rendering it on a ngFor* loop. And if you're using an ngFor* loop, use the async pipe instead ngFor="let entry of Collection | async"
I don't think that's possible. When you subscribe to an Observable it handles values as a "stream", not necessarily a loop. I have seen some makeshift helper methods that handle the data synchronously, though the time it takes to subscribe is not decreased. Check out this article for an under the hood look... A simple Observable implementation
However, you can set it up to only loop once.
The way that I've been setting up that scenario, the collection only gets looped through one time (in the constructor when the app starts) and detects changes in the collection. In your case it would look like:
values: YourModel[] = []; //this is an array of models to store the data
theData: Observable<YourModel[]>;
errors: string[];
subFinished: boolean = false;
constructor(){
this.theData = OrderDetails.find({expectedShipDate:{$in:strArr}},{fields:{"expectedShipDate":1,"loadNo":1},sort:{"expectedShipDate":1}}).zone();
MeteorObservable.subscribe('orderFilter').subscribe();
//push data onto the values array
this.theData.subscribe(
value => this.values = value,
error => this.errors.push("new error"),
() => this.subFinished = true
);
}
The "values" array is updated with whatever changes happen to the database.

Can I pass always the full state to reducers?

Is there any inconvenient at all if I design my reducers to, instead of reading only the partial state, had access to the full state tree?
So instead of writing this:
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state.a, action),
b: processB(state.b, action),
c: c(state.c, action)
}
}
I destructure state inside doSomethingWithA, c or processB reducers, separately:
function reducer(state = {}, action) {
return {
a: doSomethingWithA(state, action), // calc next state based on a
b: processB(state, action), // calc next state based on b
c: c(state, action) // calc next state based on a, b and c
}
}
Would I'd be using more RAM? Is there any performance inconvenient? I understand that in javascript, a reference is always passed as parameter, that's why we should return a new object if we want to update the state or use Immutable.JS to enforce immutability, so... again, would it be of any inconvenient at all?
No, there's nothing wrong with that. Part of the reason for writing update logic as individual functions instead of separate Flux "stores" is that it gives you explicit control over chains of dependencies. If the logic for updating state.b depends on having state.a updated first, you can do that.
You may want to read through the Structuring Reducers section in the Redux docs, particularly the Beyond combineReducers topic. It discusses other various reducer structures besides the typical combineReducers approach. I also give some examples of this kind of structure in my blog post Practical Redux, Part 7: Form Change Handling, Data Editing, and Feature Reducers.

Resources