Redux/sagas: Approach for larger API's - javascript

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.

Related

Can a nuxtjs application store be something dynamic?

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!

Is breaking up React global state into pieces a good idea?

I'm constructing a React App that is basically a photo sharing app.
Here's some use cases:
User can upload photos and videos
User can see the photos in a list view
User can reorder the photos in the list
User can select photos from list and performs actions based on their selection
Users can attribute properties to these photos, such as message, title, etc. Lets call an image + its properties a Post
Here's some major architectural components:
A CDN service to upload, host, and transform image and video creation
A backend application paired with a DB for persistent storage
I'm looking for a good way to organize all this data in state. One thought I had was to break up the state into separate, simple data structures.
Roughly this:
posts <Array> maps Post index to Post ID
media <Object> maps Post ID to Image Urls
selectedPosts <Object> maps Post ID to Boolean
loadingPosts <Object> maps Post ID to Boolean
So here we have four data structures.
posts: Determines what posts are in state and in what order
media: Attributes Post IDs to Image URLs
selectedPosts: Determines what posts are selected
loadingPosts: Determines if a given post is loading or not
I'm surfacing these via four React Contexts
Breaking up state into separate contexts makes it really easy for dependent components to subscribe to exactly the state they need. For example:
import React from 'react'
import useMedia from '/hooks/media'
export default ({ postId }) => {
const { media } = useMedia() // useMedia uses useContext under the hood
const imageForThisPost = media[postId]
return (
<Image src={imageForThisPost}/>
)
}
What I really like about this is that this component gets exactly the state it needs from global state and really only has one reason to re-render (pretend i'm using useMemo or something). I've worked with some tough React Redux web apps in the past where every component re-rendered on any state change because all the state was in one data structure (albeit memoized selectors could have fixed this).
Problems arise when it comes to use cases that impact multiple contexts. Take uploading an image as an example:
The sequence of events to upload a photo looks like this:
An empty post with ID, "ABC", is selected. Update selectedPosts context
User uploads a file and we wait for CDN to return image url
Update loading context of post ABC (loading == true)
(receives image url)
Update posts context at ABC
Update media context at ABC
Update loading context of post ABC (loading == false)
Deselect post ABC. Update selectedPosts context
Long, intricate, async sequences like this are tough to deal with, encapsulate, reuse, and test.
What's a better way to organize state for medium sized web applications like this with potentially long sequences of async actions and somewhat complex state?
Wishlist:
Easy to control re-renders
Easy to add extend/change app functionality (not a fan of huge deeply nested data structures)
Easy to test
Does not use Redux (but useReducer is fine) (I just don't like the huge overhead that comes with redux)
Anyone have any thoughts?
I know one way might be to emulate Redux using useReducer, actions, and selectors. And thankfully dispatch is a stable function identity in React. Idk, I just really don't like dealing with big, deeply nested objects. When product requirements change, those are such a pain to deal with because the entire application depends on a particular schema shape.
Old post, but am assuming that selectedPosts and loadingPosts are filtered posts objects.
Personally I would probably not have them as separate, but filter of posts which are marked loading / selected and upload those in code via some action?
I.e. state has list of posts, with loading / selected props and code does the filter on posts to upload - are you over complicating it? Presuming with some lookup one to many / many to many? on state for media to create the association? What did you end up doing? I would be using useReducer and dispatching updates to state, not sure context is needed unless its deeply nested.

Passing values between components: Pass References vs Subjects

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.

Loading configurational data during page load in react app

I'm working on a React application that is connected to a few ASP.NET Core WebAPI microservices. Each of these services have different entities that are used throughout the application.
Within the complete application, there are enums and 'configurational data' that can be configured.
Imagine configurational data as just simple tables, with two fields (Id and Value).
Different entities have FK relationships to the configurational data, and/or have enum fields. I'm trying to understand how I would, in a performant way, can load the configurational data and all the used enums upfront upon page load, so that these can be used in dropdowns. I'm pretty new to React (1 month), so still learning day by day.
I've initially taken the approach of writing a custom DropDown component that accepts a WebAPI GET url, to fetch the possible values for a certain table or enum, but it's very impractical and will prove to be not so performant once there are 1000 users using the application, and all doing calls to these api's multiple times, just for some dropdowns.
So, what is the advised approach to have some sort of splash screen in React AND call APIs to cache values, that then can be used in other components?
"I've initially taken the approach of writing a custom DropDown component that accepts a WebAPI GET url"
You should not do this :)
Before I suggest a solution I want to go through a couple of important key concepts.
Firstly
The render method will always run once before you async stuff happens (like your GET).
Lifecycle methods order which will trigger the First Render : constructor => componentWillMount => render => componentDidMount.
This means that you will have to have all your data ready for render initially. Or have conditions which prevents certain jsx for being called.
Secondly
If you have dynamic content, which will be the options in your dropdown, you'll have to get it from somewhere. If it's static you can define a list locally.
If you want to save the response you could use localStorage or if you are using redux; the middleware redux-persist to persist the store.
I personally don't see the purpose though, because if the dynamic options updates you would want that to update the application state. And even 1000 simple calls like that is not expensive for the server.
If you are using redux, you should keep the options there, because then you won't have to make an GET every time you're mounting the component with the dropdown.
Suggestion:
Many ways you can do this but here is a simple solution).
keep a local state in component and initialize it for first render
this.state = {dropDownOptions: []}
Then in componentDidMount make api call:
fetch(url).then((response)=>this.setState({dropDownOptions: response}));
And lastly in your render method:
<MyDropDown options={this.state.dropDownOptions} .../>

Flux architecture misunderstanding in example chat app

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.

Categories

Resources