question on reloading observable data in mobx upon page load - reactjs

I have a typescript DataStore object that stores some data (using Mobx) from the server when I login. For example:
import { observable } from 'mobx-react';
class DataStore {
#observable user: User | null = null;
lots more code here
}
The DataStore class has a doLogin method, and when that is successful it populates the user object with data of type User (which is an interface I defined elsewhere - it contains a number of user data fields like username, email etc).
Now, when or if the user refreshes the page or tab, the stored User data goes away. This of course will cause problems if I try to use it.
So what is the best pattern to use here? One thing to do is to save this data in localStorage or sessionStorage or localForage, but I have heard some horror stories about this not working in some edge situations. Another possibility is to check to make sure it exists and if not then use the dataStore object.doLogin method to reload it.
What do most developers do in this situation?

I don't think this is a mobx problem, rather a general strategy on how to fetch data.
If you choose to back up the data locally, then make sure you version the data, so when your code or data model changes, you can skip the local data and fetch again. This way you won't have any problems with locally saved data.

Related

React-Query/SWR vs Global state (e.g Redux, Zustand) when updating a single post creator's details in an infinite list of posts

In my application, I have a paginated feed containing posts retrieved from the /feed endpoint.
Each feed post has...
postId
postTitle
postBody
postCreator (object)
Each postCreator object has...
userId
userName
userBio
userImageUrl
As suggested by Redux, I'm currently...
Extracting the postCreator objects and storing them in their own global usersStore (using Zustand)
Replacing the postCreator objects with the relevant postCreatorId (which references a user in the global usersStore) in each post
Storing the posts in their own postsStore (using Zustand)
The benefits of this are...
If a user updates their information (e.g. name or bio), we only have to update their record in the usersStore, and their information will be updated across the whole app (their post creator info in the feed posts UI, their profile image in the tab-bar etc)
We don't have to re-request the data from the /feed endpoint to get the posts containing the updated postCreator object. Especially considering that a user may have scrolled way down the feed at that point in time, so we may have to get hundreds of posts from the /feed endpoint, putting strain on our backend.
React-Query/SWR
I have recently discovered React Query and SWR and have heard a lot about how they "remove much of the need for global state", but I'm slightly confused by how they handle the above scenario. After doing some research on React-Query, I'm under the impression that there a few ways to handle this...
Still use the global usersStore and postStore to update the user's information in a single place, as described above.
Scrap the global usersStore, and update the postCreator information directly in React-Query's cache for the /feed infiniteQuery, using queryClient.setQueryData. But we would have to also update the other query caches where the user's information is returned, and what if the cache has expired?
Scrap the global usersStore, and refetch all of the posts from the /feed endpoint when a user updates their info. However, if a user has scrolled way down the feed at that point in time, we may have to get hundreds of posts from the /feed endpoint, putting strain on our backend. React-Query infinite query refetch docs
What is the best practice way to handle this using React-Query/SWR?
react-query doesn't have a normalized cache. That being said, mostly, there is no need to do any normalization - just keep what the api sends you as your state, retrieve it via useQuery, and get rid of all redux / zustand global stores that would otherwise hold your server state.
In your example, I see two queries:
useQuery(['user', id]) for the user data
useInfiniteQuery(['feed']) for the feed of posts
When rendering your feed, you'll likely render a Post component for each entry, which can in turn call the useQuery that retrieves the user data. If you are worried about the n+1 queries that are being made, and you are in fact already delivering the user-data with the feed, you can manually prime the user-cache in the onSuccess callback of the feed query - just take the user data and copy it to the user-cache.
That way, the user-cache becomes the single source of truth for user data, which also updates from the feed query.

Use cases for when not to use redux?

I was always wondering if I should use redux store all the time even when it's not really necessary.
For example:
I have form with select field that has some options I fetch from API. Let's imagine that form is for adding new car listing and select options are car models. Should I directly call API from component and display options or should I create store CarModels or something like that and store results from API there and then map those values to state in component?
I have "Favorites" feature, when you click heart next to some item (let's say a car), do I need to go through dispatching all events FAVORITE_CAR_REQUEST, FAVORITE_CAR_SUCCESS etc... or is it good enough to just call API directly from component (using some kind of api-service of course).
It's related to question above. If I have screen where I show favorites, I should then probably have store and connect everything with actual favorite action so I update the list. On new favorite, API will return favorited item, should I push that one in list of favorites already in store or should I just load again latest list when user opens up the favorites screen?
Thanks in advance.
I think this is a perfectly valid question. What I feel like you're trying to ask is if you could/should mix react state and the redux store. The answer is sure! Just think about where you need to use that part of state before deciding where to store it. If you need a part of the state in multiple components, it probably makes sense to use Redux. If you only need state locally, perhaps to set form validation errors, maybe use react's state management if you feel like it. React and redux are both meant to be flexible, so as long as you're consistent in when you use the redux store and react state you should be good.
I found this article that also explains this pretty well: https://blog.jakoblind.no/is-using-a-mix-of-redux-state-and-react-local-component-state-ok/
I tend to use redux when the state has to be accessed globally / complex logic that i want to be logged properly

Load all documents at once. Not progressively

When I run a .fetch() command, it first returns null, then say suppose I have 100 documents and it will keep on loading from 1 to 100 and the counter keeps updating from 1 to 100 progressively. I don't want that to happen. I want all the results to be displayed at once after the fetch process has been completed.
Also, how can I display a relevant message to the user if no documents exist? The fetch method doesn't work for me since it returns 0 at first and hence "No document found" flashes for a second.
dbName.find({userID:"234234"}).fetch()
Even though the above has 100 docs, it first shows null and then keep loading the documents one by one. I want it load all at once or just return something if no docs exist.
I don't want that to happen. I want all the results to be displayed at once after the fetch process has been completed
To really obtain all documents at once on the client you will have to write a Meteor Method that returns all the documents:
Meteor.methods({
'allDocs' () {
return dbName.find({userID:"234234"}).fetch()
}
})
Note, that you have to call fetch on the cursor to return the documents, otherwise you will face an "unhandled promise rejection error".
Then call it from the client as usually. You can even add the documents to your client side local collection without affecting allow/deny (which should be off / deny all by default):
Meteor.call('allDocs', (err, documents) => {
// ... handle err
// all client collections have a local collection accessible via ._collection
const localCollection = dbName._collection
documents.forEach(doc => localCollection.insert(doc))
})
Advantages:
Returns all documents immediately
Less resources consumed (no publication observers required)
Works with caching tools, such as ground:db, to create offline-first applications
Disadvantages:
You should limit the query and access to your collections using Methods as much as possible, (using mdg:validated-method) which can require much more effort than shown in this examples
Not reactive! If you need reactivity on the client you need to include Tracker and reactive data-sources (ReactiveVar etc.) to provide some decent reactive user experience
Manual syncing can become frustrating and is error prone
Your question is actually about the subscription and it's state of readiness. While it is not yet ready, you can show a loading page, and once it is you can run the .fetch() to get the whole array. This logic could be put in your withTracker call, e.g.:
export default withTracker((props) => {
const sub = Meteor.subscribe('users');
return {
ready: sub.ready(),
users: sub.ready() && Users.find({userID: props.userID}).fetch()
};
})(UserComponent);
Then, in your component, you can decide whether to render a spinner (while ready == false), or the users.
Your question is not entirely clear to me in terms of tools etc (please state which database connector lib are you using), but firstly, given you're doing a database access, most likely, your ".fetch()" call is not a sync function but async, and also most likely, handled by a promise.
Secondly, given that you're using react, you want to set a new state only after you get all the results back.
If fetch is a promise then just do:
dbName.find({userID:"234234"}).fetch().then(results =>
setState({elements:results.data}) // do your processing accordingly
}
By only calling setState inside the promise, you'll always have all the results fetched at that instant and only with that do you update your component state with the setState function - either using your react component class this.setState or with hooks like useState (much cleaner).

React Redux - state undefined on first render

For my client I'm creating something like quiz web app in react with redux based on websockets (socket.io) with a huge, very unique data. No user interaction, just presentation layer. It works like this: I get websocket event with url to my layout and payload data, and then I render given url and fire redux action with data as argument, which becomes app's state. Simple as that. BUT I noticed that on first render initial state is loading, not given from websocket as argument to action. As I said data I get is huge and unique so I didn't want declare in reducer something like this:
pageData: {
assets: [],
questions: [],
details: []
And so on. It's much more complicated btw it's just an example. Instead of this I made something like this:
pageData: {}
And I was hoping that on view (using connect) I can get this data like this:
this.props.view.pageData.questions
But then it turned out that I can not get this because it's undefined on first render. So my questions are:
Is there a way to access to this data on first render without
declaring whole structure?
If not, should I reconstruct given data in reducer?
Should I then create reducers for each page (there are like over 20 views
with unique data)
Of course I can declare everything in reducers but I feel it's very hard to maintain so much data.
But you know, maybe I'm just too lazy and I should declare initial state for each page and this question does not have sense ;).
I think you may have a few options here:
Define fallback data in your components if undefined
Don't render your page (or components) until you have received the data
Define your initialState explicitly as you already suggested
All or most your components expect or should expect data of a certain kind and in a certain format. For this reason, laying out the structure beforehand (#3) seems to be most appropriate. Ask yourself this: would my app still display correctly if the format of the web socket event data changes?
To answer your questions specifically:
Is there a way to access to this data on first render without
declaring whole structure?
Yes, you could use the || operator in your bindings to fall back (#1) to an empty array or object or value. Example <MyComponent listOfItems={this.props.items || []}. This effectively creates an empty state, however, IMO this should be standardized in the reducer/store with initialState.
Should I then create reducers for each page[?]
Not necessarily a reducer for each page, but a store with all pertinent data to your application. It is hard to say for sure without knowing more about the architecture of your app, but keeping small, well defined chunks of information is generally easier than one big blob.
I strongly advocate defining your data beforehand. It might sound cumbersome at first, but it will pay off greatly and helps others understand what the app might look like with live data.
that's because you haven't added default case in reducer
default:
return state;

How to manage data in a React/Redux application

I having some issues on how to conceptualize a react application with Redux being my Flux library of choice.
So I am taking a few assumptions from my readings, correct me if I am wrong,
How does one manage data fetching?
Let's say this, I have a application that needs to fetch some data specific for the current logged in user, I assume this user data should be stored in the Redux Store.
But now the problem ensues, if all my state data is stored in a store, do I have for example a array of messages in the store for this user, and make my component fetch the information from the store? Or should I Fetch the data on the componentWillMount or similar method? I get that when I need to fetch data the first time, I will send an action to the store to fetch the data from the server, which will trigger a change event that I can catch on the component and update the state, is that a correct?
I feel like I am missing a point somewhere and can't make the connection on how the app is supposed to be structured and manage the data, since it seems the store will be bloated with tons of smaller "state" objects that will be used across the other routes/components.
In Redux you have a single store with many reducers. Each reducer changes specific part of the state.
For example you have an state like this:
{
messages: [...],
currentUser: {...},
notifications: [...],
...
}
In this case you will have a reducer to change messages part of state. Another reducer to change notifications part.

Resources