When we work with an API, logically, we should split our "store" folder into several files, cutting our logic across our different entities.
For example, an API that deals with users as well as books.
Store/
books.js
users.js
It is VERY likely that at some point in our application, we will have to search the data in bulk. (Either all users or all books). Or more specifically (only 1 book, only 1 user; identified by an ID).
My question is the following:
Can I afford to make fewer files in the store, if the methods I use to retrieve the books are also used to retrieve the users?
For example, I could have a generic method, which would fetch "entity" passed as a parameter.
async genericFetch({commit}, entity) {
const req = await this.$axios.get(`/${entity}`)
commit('setData', {entity, values: req.data})
}
I will use this method when mounting a component, e.g.
We can imagine the same thing with getters or mutations (we can imagine that in my example, the commit setData will precisely modify the data according to the entity passed as a parameter as well).
I'd be happy to know how you organize your store.
According to some projects, it's not uncommon to have to repeat ourselves through the different files in the store, only because we're not going to work with the same data, although we're doing pretty much the same thing.
Nevertheless, the question I'm asking suggests a real problem of clarity in the code (and therefore, I imagine it would be more complicated to maintain the code).
Thank you for your feedback!
Related
tl;dr: Why not pass variables by reference between components to have them work on the same data instead of using e.g. BehaviorSubjects?
I'm writing a sort of diary application in Angular 8. I have two components (Navbar and Dashboard) and a service (EntryService).
Navbar lists the entries, Dashboard provides the textarea, EntryService glues them together and communicates with the database.
While debugging the application I stumbled upon a way to communicate between the service and a component that i haven't thought of before.
By accident I passed a variable (entry: Entry) from the Dashboard by reference to the EntryService. The service saved to the database getting a unique ID back and saving this ID into the entry variable. This change immediately reflected to the Dashboard because of the 'passing by reference'.
Until now I was using Subjects to update the components on changes, but passing references around seems to be much simpler, because I want to work on the same data on both components and the service.
I've studied Angular for a while now and not read about this approach, so I'm wondering if it is a bad idea or design and if yes why?
Thanks for your answers!
Passing by reference can be handy. But as a general approach to keep the application components in sync it has some draw backs. With Subjects you can easily investigate in which places of the application the value of the Subject will be changed by checking where the Subject.next() function is being called. When you pass your object by reference to a hundred components/services it will be much more difficult to find out, which of them modify the object and more importantly when, becaue often you want to trigger other changes afterwards. When you subscribe to Subjects, you get notifications about changes and can react to them. Subjects and Subscribers are an example for an Observer/Observable pattern, it allows you to decouple your application logic. And they are much more flexible, for example you can have a Subject which can return the last x number of changes or you can debounce the changes when you track user input, you can apply filters to them etc.
So let's say I'm making a React Redux app for handling a library. I want to create an API for my backend where each model (book, author, etc) is displayed in the UI.
Each model does not provide a public constructor, but a from static function which ensures that only one instance per id exists:
static from (id: string) {
if (Books.books[id]) {
return Books.books[id];
}
return Book.books[id] = new Book(id);
}
Each model provides an async fetch function which will fetch its props using the backend. The advantage is that there is no thousands instances, also I don't have to fetch twice (if two parts of my app needs the same model, fetch will actually be called only once). But I fail to find any drawbacks, except that there might be a discrepancy between a code that fetches its models and one that assumes they are still not fetched, but I still don't see when it would really be an issue
But I fail to find any drawbacks
I see at least two :
The singleton pattern is an anti pattern.
Static factory methods don't provide explicit dependencies.
Mocking the method in unit tests or switch to another implementation will be harder.
You don't have cache size limitation.
For short lists, it is OK.
But if you may cache many objects, you should keep only last recently used instances.
I can think of two problems:
Are your models mutable? If you change a property of the instance, it would reflect everywhere that instance is used. That might either be desirable, or not at all. And with that from method, you cannot do anything about it.
If your models are immutable, sharing instances is in fact a common practice, also known as hash consing.
Your implementation leaks memory like hell. The instances will stay referenced from that books array/object even if they are no longer needed.
I've worked with Redux/sagas workflows on small projects based off of this real-world example, but the logic of those is not nearly as complex. How should I be approach working with a more comprehensive api (i.e., Reddit's API), without making things overly verbose?
Do I make a const for every endpoint? i.e.,
export const fetchUser = login => callApi(`users/${login}`, userSchema)
Should I be worried about managing the entity cache?
Is there a way how to further reduce complexity/boilerplate (i.e. further grouping request types with get/put/post/delete for the same endpoint)?
Are there any examples out there that deal with bigger/more complex than the real-world?
I think the answer depends on how fluid you want your components to be.
I'm working on a large codebase using sagas, our pages are separated into "types", for example a "list" type, "form" type etc.
We have one saga responsible for fetching content, while each pageComponent when being rendered is responsible for supplying the endpoints.
this allows a very modular approach, to add a component you need to deal with one subsection of your file system.
Our pages are mostly a configuration file that contains all this information, and we use this configuration to render a "generic" component with the correct data.
Saga reusability
I see Sagas as sequential processes, they can be for async fetching data, but they're also useful for anything that needs to be dealt with in sequence.
These "Flows" are sometimes very similar in a codebase, and those are the ones you want to generalize.
Like you said, the most common operations are CRUD for any endpoint, that can be easily grouped together.
Login is extremely different than loadUserList and different things need to happen afterwards, however loadUserList and loadRepoList is extremely similar.
Things that impact reusability
Your ability to control your API's, if you can dictate the shape of the API you consume, you can get away with even more generalizations in front end.
The shape of the application(Front-wise) - are your pages strangely dependent on one another's state? for example it's not uncommon for insurance programs to have forms that link to one another, you can fill the first 3 forms in any order you want, but once all three are complete the 4th unlocks.
Each of these dependancies will normally have their own saga that controls the flow of your use story.
Does your application require syncing? You can easily create sagas that automatically sync data with your different endpoints and update your Redux State, there's much to consider here, including if we decide to interrupt the user with new data(we might want to let him know that the form he's editing has out dated data) - syncs require a distinct saga as there are usually various business rules when to sync what data - if the rules are very different this can force you to create multiple sagas)
Common Sagas that can be unified
UserSagas - login, logout.
FetchData - fetch a single record, or a collection.
DeleteData - Delete a single record, or a collection of IDs
Data Syncing - Updating your local data from a remote periodically.
Regarding the entity cache
Entity cache is just a name they picked, but this goes back the points mentioned before.
Does your application run on stale data, or do you fetch from the server every time your component is loaded?
If the data is only fetched once and you display stale data, you'll store it in a type of cache(that's basically the redux store).
If you show stale data, this is the way to go.
I'm still trying to wrap my head around frontend state. Is there a common best practice for setting up stores for a resource? For example, my web api has:
GET /bikes
GET /bikes/:id
I started off with just a BikeStore and bikes: []. Now I'm working on the ShowBike component and not sure if I should use the BikeStore (not exactly sure how) or make a second store for single items.
The store concept in Flux is rather simple abstraction on the client how you get access to the data. Separate stores should be used for different kinds of data. In your case the resource is the same, there is not any good reason to keep separate stores for bikes. Even more: stores for single items is not an intended usage and should be avoided.
From the flux docs:
Stores contain the application state and logic. Their role is somewhat similar to a model in a traditional MVC, but they manage the state of many objects — they do not represent a single record of data like ORM models do. Nor are they the same as Backbone's collections. More than simply managing a collection of ORM-style objects, stores manage the application state for a particular domain within the application.
I'm trying to understand the Flux example chat app. The authors mention this unidirectional data flow:
However, in the example app there are dependencies between Action Creators (ChatMesssageActionCreator) and Stores (MessageStore), and between Stores (MessageStore, ThreadStore) and Web API Utils (ChatMessageUtils), which seems to be against the unidirectional data flow rule:
Is it recommended to follow the given example, or should one design a better pattern?
Update
I figured out that the ChatMessageUtils doesn't belong to Web API Utils, so the two arrows from store shouldn't point there, therefore maybe they're okay.
However the connection between the ActionCreators and the Store seems still strange.
The example is a bit forced, and it was created with the purpose of trying to show how waitFor() works. The WebAPI aspect of the example is pretty half-baked and really should be revised.
However, even though MessageStore.getCreatedMessageData(text) passes a value to the store, it's still a getter. It's not setting data on the store. It's really being used as a utility method, and a good revision (pull request?) would be to move that method to a Utils module.
To improve upon the example for the real world, you might do a couple things:
Call the WebAPIUtils from the store, instead of from the ActionCreators. This is fine as long as the response calls another ActionCreator, and is not handled by setting new data directly on the store. The important thing is for new data to originate with an action. It matters more how data enters the system than how data exits the system.
Alternatively, you might want to have separate client-side vs. server-side IDs for the messages. There might be few advantages of this, like managing optimistic renderings. In that case, you might want to generate a client-side id in a Utils module, and pass that id along with the text to both the dispatched action and the WebAPIUtils.
All that said, yes the example needs revision.