Firebase: referencing docs with a string vs. referencing docs with a ref. Is there a difference? - reactjs

This is more of a general question, I've been bouncing around multiple posts on StackOverflow and reading what I've found so far in the docs. I haven't found anything super concrete yet to answer the post question which is: what are the benefits of linking documents to one another through the ref type vs. the string type?
As of now, I'm converting all my string "refs" to properly typed "refs". However, since i'm still relatively new to the platform I'm scratching my head wondering if this is even necessary. I assume I'd be just as effective at finding related docs with the string as with a reference.
Also, for the sake of future readers as of me posting this, you can set a ref like so:
db.collection(...).add({
...
reference: firebaseFirestore.doc(
`lesson_translations/${translationID}`
),
// reference is now typed as a 'ref'
})
I had found other posts on stackoverflow accessing it with .doc(...).ref which doesnt seem to be a thing anymore.

It's mostly a matter of personal preference and perceived convenience. There is not really anything a reference type can do that a string and your own code could not also do.
Having a reference type mostly saves you the trouble of building a new reference object in your code, if that's what you would have done with a string document ID anyway. You can also use it in security rules for the same purpose when it comes time to use get() to fetch another document referenced by a field.
Again, it's personal preference. Do whatever is most convenient for you.

When you call a document reference, you instantly get the document details while if you take a string path and call it, it will take some time to find that data. It is just a matter of a few milliseconds and a few lines of code. Imagine if you had a list of document references to store and you store them as strings, and then had to call all of them looping over it

I know of one tangible benefit, but it's only relevant if you are using a library for data binding that supports loading nested documents to n depth. I personally am using vuefire, but I would assume something similar exists for the react ecosystem.
It allows you to e.g. bind a collection or document to a variable, and automatically sync all the data of referenced documents.
Example
You sync a collection('users') with a depth of two, and each user document contains an array of favourites:
{
name:'John',
favourites: [ref1, ref2, ref3]
}
The library would immediately download the references and replace the original ref files with the data:
{
name:'John',
favourites: [{title:'foo'}, {title:'bar', {title: 'baz'}]
}

Related

Why can't React use a CONTENT to automatically generate keys?

That's obvious that for the diffing algorithm in React keys are essential. But I was wandering, why React just can't automatically generate keys based on the content we iterate over?
I also assume that items can share some similarity, or cab be identical in terms of content, but isn't it possible to generate keys once user open a page and somehow attach them to the items, so it is stable?
Or maybe there where attempts to solve the problem, if so, I would be grateful if you share it to me.
Update
Thank you guys for your answers, I've learnt a lot!
Also a thing I had in mind: what we developers do when there is no stable id (e.g. user added an item which is not yet saved into DB). In the cases we just generate id, and attach it to the object, or element in an array, but we do not generate ids on a fly, so it remains stable over time.
What if React just generate ids for all arrays which are involved into rendering process, in other words, arrays which are directly used in render function?
It can be done only once, during phase Commit phase, or whatever. Also I believe, the id can be readonly, or something, so user can't erase the id.
p.s.s
While I was writing p.s. question above, I realized, autogenerating id for arrays wouldn't work, since I've missed two things. All side effect react can do only during the Commit phase, but not Render phase. But that's not the main problem.
The main problem is when we use filtering or sorting on a back-end side. Since we receive a new array, filtered one, we would need to regenerate ids for those elements, but basically, that's the same html elements, in which we can change content to match filtering order. That's the same as Slava Knyazev mentioned.
React can't generate keys, because the entire point of keys is for you to help React track elements during it's tree-diffing stage.
For example, lets say you have the following code, where you naively use content instead of identifiers for your keys:
const people = usePeople(); // [{ id: "1", name: "James"}, {id: "2", name: "William"}]
return <ul>{people.map(p => <li key={p.name}>{p.name}</li>}</ul>
The above code will function and behave as you would expect. But what happens if the name of a person changes? To understand it, lets look at the tree it generates:
ul
li(James) James
li(William) William
If James becomes Josh between renders, the new tree will look like this:
ul
li(Josh) Josh
li(William) William
React will compare the two results and conclude the following:
li(James) is to be removed
li(Josh) is to be added
However, if we set our key prop to p.id, then the old and new tree will look as follows, respectively:
ul
li(1) James
li(2) William
ul
li(1) Josh
li(2) William
And when React compares the two, it will identify that James has become Josh, and needs only the text adjusted.
In the first scenario, the <li> component is completely destroyed, and a completely new component takes its place. Both of these actions run a complete React lifecycle for the component. In the second, it remains untouched, and only the text inside changes.
While in this contrived scenario, the performance penalty in the first case in minimal, it may be very significant with complex components.
I believe, unless your data is 100% certainly going to sort in one way and never change, key={index} isn't a good key (which is what I assume you want your auto-generated keys to be). You'd ideally want something that is unique to each item, regardless of the order.
It's explained in more detail in the new beta react docs https://beta.reactjs.org/learn/rendering-lists#where-to-get-your-key
I think what you are implying is React could potentially choose to use something like a stable hash (say sha1 on a serialised string or something) on the object to generate a unique key. I think this actually would work in many cases, and even gave me pause for thought for a while! Your question is actually a really good one, and a deep one.
However, it wouldn't work in every case. I think it would work only on a static object which has no methods or anything attached. On a JS object, not all properties are enumerable. Hashing something could only ever happen on the enumerable objects of properties, but the dev may have non-enumerable yet-still-unique methods attached to these objects. In fact, even enumerable methods cant really be serialised reliably and they could be what makes the object unique. Not to mention the complexities of reliably hashing something with prototypical inheritance involved.
I suspect there's also a performance aspect to this. Hashing is cheap, but no that cheap. Most cases can be keyed by just referencing a unique ID in the object, which is vastly cheaper. When enumerating a very large number of objects, these things matter, and so its better to defer to the developer. After all, if you really do want to hash it, its just one function call in userland -- and this saves great confusion on developer side when it doesn't work. The Principle of least astonishment comes to mind.
There's also an aspect of how this would limit the power of how expressive JSX can be due to it basically allowing free-form JS. You would probably have to supply some low level <React.Map> component primitives in order for react to supply this default key handling which implies you are a bit more restrained on what you can and can't do (complex functional chains).

Question in Redux's doc with regard to createSelector and createEntityAdapter

On the page:
doc
This section:
states that dispatch(reactionAdded()) won't cause the component to re-render as posts is not changed here. But it seems reactionAdded does mutate one of the posts cause the entire posts array to be replaced by a new one.
Another question:
Does it mean value returned by selectById selectIds from createEntityAdapter will always return the same array(unchanged reference) as long as all ids of entity are unchanged even if content of entities memeber is changed?
Hmm. I wrote that tutorial, and it's entirely possible I made a mental mistake when writing that example. Yeah, reactionAdded does update a post entry, which would also mean that there should be a new posts array generated as a result because of the immutable updates. Probably need to come up with a different example there :)
And yes, selectById always does a straight lookup - ie, return state.posts.entities[postId], so there's no derived data involved. It just returns that one item by itself.

Should elements ever be made available outside of a page object?

This is a question I cannot find a definitive source on and am hoping to get some answers based on users previous experience mainly with explanations as to why a certain approach DID NOT work out.
I am using webdriver for automation via Protractor and am having a debate on whether or not page elements should ever be made available outside of page object itself. After researching it appears that there are a few different approaches people take and I cannot fully grasp the long term implications of each.
I've seen the following different implementations of the page object model:
Locators are declared in the page object and exported
This is my least favorite approach as it means element are actually being identified in the test. This seems like a bad standard to set as it could encourage automaters to use new locators, not from the page object, directly in the application.
Also any which require any dynamic information cannot be directly set when PO is initialized require further editing.
pageobject.js
export default class HomePage {
constructor() {
this.passwordField = '#password';
this.usernameField = '#user';
}
}
test.js
const homePage = new HomePage();
$(homePage.usernameField ).sendKeys('admin');
$(homePage.passwordField ).sendKeys('password');
Elements declared in page object and exported, locators not
pageobject.js
export default class HomePage {
constructor() {
this.passwordField = $('#password');
this.usernameField = $('#user');
}
}
test.js
const homePage = new HomePage();
homePage.usernameField.sendKeys('admin);
homePage.passwordField.sendKeys('password);
Elements declared in Page Object and only used directly within the page object, only methods exported
This is the approach I have used in the past and we ended up with many, many functions. For instance we had setUsename(), getCurrentUsername(), getUsernameAttibute(), verifyUsernameExists() and same for password element and many other elements. Our page object became huge so I don't feel like this is the best approach any longer.
One of the advantage however is that our tests look very clean and are very readable.
pageobject.js
export default class HomePage {
constructor() {
var passwordField= $('#password');
var usernameField = $('#user');
}
setUserName(name){
username.sendKeys(name);
};
setPassword(password){
passwordField.sendKeys(password);
};
}
test.js
const homePage = new HomePage();
homePage.setUsername('admin');
homePage.setPassword('123');
I'm very interested to get some feedback on this so hopefully you can take the time to read.
I prefer and believe that the last approach is the best.
Keeping aside the fact that we are talking about automation, any good/great software has following traits.
It is composed of individual modules/pieces/components
Each individual module/piece/component is cohesive in terms of data/information (selector, webdriver API calls in case of automation) specific to its API/methods to interact with the data.
The last approach provides just that with the added benefit of test cleanliness that you pointed out.
However, most of the times, for whatever reasons, we tend to ignore the modularity and make the existing POs bloated. This i have seen and was part of. So, in way, POs getting bloated is not because of the approach but the way automators/testers/developers being conscious to keep POs modular, composed and simpler. This is true whether it is about POs or the application functional code
Boated POs:
Specific to the problem of bloated POs, check if you can separate out the common elements out of the POs. For ex. Header, Footer, Left Nav, Right Nav etc are common across pages. They can be separated out and the POs can be composed of those individual pieces/sections.
Even within the main content, separate common content (if its across two or more pages if not all) into their own component API if its logical and can be reused across pages.
It is normal that the automation specs perform extensive regression for ex. an element is a password field, length of the text box is so and so etc. In such cases it doesn't make sense to add methods for every single spec use case (or expect). Good thing is, here also the commonality takes the centerstage. Basically, provide API that is used across specs and not if its used in one spec, literally.
Take for ex. a password field should be masked. Unlikely that you would want to test that in multiple spec files. Here, either we can add a method for it in LoginPO like isPasswordMasked() or we can let the password field accessible from LoginPO and the spec do the actual check for password type field. By doing this, we are still letting LoginPO in control of password field information that matters to other API (login(), logout() etc) i.e only PO knows how and where to get that password element. With the added advantage of pushing spec testing to spec file.
POs that expect/assert
At any point, its not a good idea to make any testing (or expect) to be part of the PO API. Reason(s):
The PO and its API is reusable across suites and should be easy for anyone to look at it and understand. Their primary responsibility is to provide common API.
They should be as lean as possible (to prevent bloating up)
More importantly, browser automation is slower inherently. If we add test logic to the POs and its API methods, we will only make it slower.
FWIW, i have not come across any web page that mandates a bloated API.
Exposing Elements in POs:
It depends on the use case i believe. May be an element that is used in only one spec can be a base case to expose. That said, in general, the idea is that the specs should be readable for the tester/developer AND for those who look at them later. Whether its done with meaningful element variable name or a method name is largely a preference at best. On the other hand, if an element is meant to have a bit of involved interaction (for ex hover open menu link) its definitely a candidate for exposing through API alone.
Hope that adds some clarification!
The last way is the correct way to implement page objects. The idea behind the page object is that it hides the internals of the page and provides a clean API for a script to call to perform actions on the page. Locators and elements should not be exposed. Anything you need to do to the page should be exposed by a public method.
One way you can avoid a getter and setter for each field on the page is to consolidate methods. Think about actions that the user would take on the page. Rather than having .setUsername(), .setPassword(), and .clickLoginButton() methods, you should just have a login() method that takes the username and password as parameters and does all the work to log in for you.
References
Martin Fowler is generally considered the inventor of the "page object" concept but others coined the name "page object". See his description of the page object.
Selenium's documentation on page objects.

What's the preferred way to go about using backbone with non-crud resources?

New to backbone/marionette, but I believe that I understand how to use backbone when dealing with CRUD/REST; however, consider something like results from a search query. How should one model this? Of course the results likely relate to some model(s), but they are not meant to be tied to said model(s).
Part of me thinks that I should use a collection using a model that doesn't actually sync with a data store through the server, but instead just exists as a means of a modeling a search result object.
Another solution could be to have a collection with no models and just override parse.
I assume that the former is preferred, but again I have no experience with the framework. If there's an alternative/better solution than those listed above, please advise.
I prefer having one object which is responsible for both request and response parsing. It can parse the response to appropriate models and nothing more. I mean - if some of those parsed models are required somewhere in your page, there is something that keeps reference to this wrapper object and takes models from response it requires via wrapper methods.
Another option is to have Radio (https://github.com/marionettejs/backbone.radio) in this wrapper - you will not have to keep wrapper object in different places but call for data via Radio.

When to maintain reference to key vs. reference to actual entity object after put operation.

When working with datastore entities in App Engine, people have noticed odd behavior after a put operation is performed on an entity if you choose to hold on to a reference of that entity.
For example, see this issue where repeated String properties mutated to _BaseValue after a put was performed.
In the ensuing discussion, in reference to a repeated String property, Guido van Rossum writes:
"I see. We should probably document that you're not supposed to hang
on to the list of items for too long; there are various forms of
undefined behavior around that."
I get the sense from this thread that it's not a good idea to maintain reference to an entity for too long after a put, as unexpected behavior might arise.
However, when I look at the GAE source code for the Model.get_or_insert() method, I see the following code (docstring removed):
#classmethod
def get_or_insert(cls, key_name, **kwds):
def txn():
entity = cls.get_by_key_name(key_name, parent=kwds.get('parent'))
if entity is None:
entity = cls(key_name=key_name, **kwds)
entity.put()
return entity
return run_in_transaction(txn)
Notice how a put operation is performed, and the entity is returned post-put. Just above, we saw an example of when this is not recommended.
Can anyone clarify when and when it is not ok to maintain a reference to an entity, post put? Guido seemed to hint that there are various scenarios when this could be a bad idea. Just curious if anyone has seen documentation on this (I cannot find any).
Thanks!
The problem described in the issue is not regarding entities, but rather lists obtained from its properties. You can hold a copy of entity as long as you like. It's just an object.
The issue is caused by some "magical" functionality provided by ndb. Let's take a look at the model definition
from google.appengine.ext.ndb import model
class MyModel(model.Model):
items = model.StringProperty(repeated=True)
What can we say about items property?
It looks like a class attribute, but metaclass logic of model.Model transforms it into an instance attribute.
What type are these instance attributes?
They can be accessed like a list of strings, but they are more complex objects having the logic required for storing and retrieving the data from datastore, validating etc.
This "magic" works well in most cases, but sometimes it doesn't. One of the problematic cases is when you get the reference to items from the instance and try to use it after put was called. Another case, mentioned by Guido, was to pass external list to initialize items property and then try to modify this property by manipulating the external list.
The thing to remember: Model properties in ndb try to behave like their basic types, but they are more complex objects. You can read more about their internals in Writing property subclasses

Resources