Maintaining an ordered list in graphql/relay - reactjs

I want to show a list of tasks in a specific order. The problem is, that the user should be able to change the order and have that saved to graphql (The user can also add and delete tasks from the lists). Currently we have implemented the list as a relay connection and add/delete/update are working fine. Here are what we are considering now:
Make graphql return an ordered list. Then copy that list to local state in the component and reorder the list there. Then just notify graphql about the changes.
Make graphql return a number on all tasks that is the sort order.
Make graphql return a linked list. So every tasks has a reference to the next in the list.
As I see it, there are some issues with each of these. With the ordered list and copying it to local state we have to handle add/remove actions somehow and update the local state accordingly.
With the number sorting order, if we use running numbers, e.g. 1..2..3.., we have to update many numbers when we want to move number 200 in between number 1 and 2. This seems like a lot of updates, and I am not sure how this is handled in relay/graphql.
With the linked list there is also a lot of bookkeeping involved in changing the order and I am not sure how this is handled in relay mutations. Perhaps returning 3 tasks from the mutation (all the tasks that have changed the pointer to the next in the list) and specify that in the "FIELDS_CHANGE" config?
Which is the best solution when using relay/graphql/react? Other solutions are very welcome.

We went down the path to create a separate mutation for moving an item in relation to another one:
The general get query returns an ordered list without any order key or such
Moving an item is done by either a moveBefore or moveAfter mutation, returning the newly sorted list. Arguments are new itemToMove and relativeTarget.
The signature for the mutation roughly looks like this:
moveAfter(itemToMove: ID!, itemToMoveAfter: ID!): Item[]
Moving an item before another item has the same signature & behavior, just the server side logic is a bit different.
As we're not using relay, I cannot comment on how to make relay aware of the changes though.

Related

MongoDB - Storing Data For Each User in User Collection or In Seperate Collection

I'm using MongoDB right now, and I'm having a hard time deciding what structure is better.
If I were to build a todo app (lol), with users who own (they all have their own) their todo list, would it be better to:
A. I have a user collection, so I store all their tasks in the User object with an array, and then update that array whenever anything changes.
or
B. I have a seperate collection which solely stores the task array, and the _id is the same as the User _id, so I can just find it in the new array and update that? (Does this mean that I will have to find it every time I make an update, slowing it down?)
Or is there maybe a better way I'm not getting? Thanks!

Subscription Refetch Updating Data

I wanted to get your opinion on something.
I'm trying to understand how a subscription works. however, I couldn't find a way to pull an array of objects in a subscription. for example, if I use createMany, I can not return all the result via subscription.
The second issue is if I return a single item for example if it's a new item, I have to "manually (air quote)" add that item to the list that is already displayed. But this feels to me I don't actually display real-time true data.
So my question is using something like
useEffect(() => {
// refetching original query when subscription is triggered
refetch();
}, [updatedNotificationData]);
would there be any downside like hitting up the server more than I should? let's say every time there is a refetching happens I might be pulling thousands of notifications (I know there is caching but still) or is there a better way to deal with bringing new data.
Also, I tried adding subscribed data to the original list but for some reason react adds 2 of the same item every time.
Thanks in advance if you can give me in the right direction.
if I use createMany, I can not return all the result via subscription.
That shouldn't be a problem if you define the return type of the subscription as array.
type Subscription{
onChange:[ObjectType]
}
It would allow you to avoid fetching again but updating cache can get a bit complicated.
Also, I tried adding subscribed data to the original list but for some reason react adds 2 of the same item every time.
In case you are using the the subscribeToMore method it's not really reacts fault but the way how the updateQuery method works: github issue regarding this.
My workaround was to subscribe via the useSubscription hook and handle the cache modifications inside the onSubscriptionData callback apollo documentation and also setting the useQuery hooks skip option once I get the data so it wont query on each rerender.

React Query useInfiniteQuery invalidate individual items

How can I invalidate a single item when working with useInfiniteQuery?
Here is an example that demonstrates what I am trying to accomplish.
Let`s say I have a list of members and each member has a follow button. When I press on to follow button, there is a separate call to the server to mark that the given user is following another user. After this, I have to invalidate the entire infinite query to reflect the state of following for a single member. That means I might have a lot of users loaded in infinite query and I need to re-fetch all the items that were already loaded just to reflect the change for one item.
I know I can change the value in queryClient.setQueryData when follow fetch returns success but without following this with invalidation and fetch of a member, I am basically going out of sync with the server and relying on local data.
Any possible ways to address this issue?
Here is a reference UI photo just in case if it will be helpful.
I think it is not currently possible because react-query has no normalized caching and no underlying schema. So one entry in a list (doesn't matter if it's infinite or not) does not correspond to a detail query in any way.
If you prefix the query-keys with the same string, you can utilize the partial query key matching to invalidate in one go:
['users', 'all']
['users', 1]
['users', 2]
queryClient.invalidateQueries(['users]) will invalidate all three queries.
But yes, it will refetch the whole list, and if you don't want to manually set with setQueryData, I don't see any other way currently.
If you return the whole detail data for one user from your mutation, I don't see why setting it with setQueryData would get you out-of-sync with the backend though. We are doing this a lot :)

Idiomatic way to chain redux state changes?

I'm trying to figure out the idiomatic way to address this:
I have a reducer that contains a list of “applied filters” and a list of “records”. When I change the filters or records, a list of “filtered records” should also change, but applying the filters to what can be a long list of records can be slow-ish.
Right now, I’m dispatching an “Add filter” or “Remove filter” action to change that list of “applied filters”. This results in recalculating the list of “filtered records” (currently done in the reducer).
I'm using two container components, one to display a list of "filter chips", the other to display the list of "filtered records".
Because applying the filters takes a while, and is done every time the filters change, the process of removing a filter from the UI seems laggy -- I click to remove a filter, and the removed filter doesn't disappear until the reducer finishes all its work, which includes updating the list of filters AND applying the new list of filters.
What I'd rather have is:
Remove the filter, and it disappears from the UI as quickly as possible. (i.e., a state change is broadcast that just includes the removed filter)
Concurrently, I'd like the process of applying the filters to occur in the background, and once that is finished, dispatch another action to change the list of filtered records.
After the filters have been applied and the action to update the filtered records has fired, I want my "filtered records list" component to update.
So, essentially I want a state change to potentially trigger ANOTHER state change, but I want to broadcast the intermediate state in between those two state changes.
I know I have to get the bulky logic out of my reducers and into an action, but I'm struggling with how/where that logic should be applied. I have that nagging feeling that I've wrapped my brain around its axle and I'm now overcomplicating things, but I can't seem to get back to the simple/correct method.
Any help would be greatly appreciated!
Edit, from a comment I added, but wanted to keep it inline here as well:
I mis-spoke in my original question, pretty big correction here --
I'm not actually calculating the "filtered record list" in the reducer -- I have a reselect selector on my "RecordList" container that takes in "records" and "filters" and returns a list of the filtered records.
I'm thinking this is more because of the "RecordList" render holding up the "FilterList" render, so I'm going to go up the component hierarchy and see if I can fix something there.
I'd still be up for any suggestions, though!

React redux: listing and editing. Should use one array of items or array of items and separate edittingItem?

If in my React app, utilizing Redux and redux-thunk, my store has an array of relatively lightweight/denormalized items that is used in a listing of said items (paginated in the API), but then I also have an edit/add option for each (which is the same item as in the listing array but with many additional fields, not de-normalized, etc.), I don't know which of the following is the best approach for storing in my store:
Maintain one array of items in my store, which contains all necessary data (normalized, and therefore each contains a relatively deep object graph). Editing simply requires finding it in the store, making the changes, and slicing back into the state.items array.
Maintain an array of minimalist items for the item listing, while also having a root state.currentItem object.
Maintain an array of minimalist items for the item listing, while resetting the array to contain only one item (containing all necessary data for editing). This avoids needing to create another reducer which may or may not be a good thing.
Because of the data-intensiveness of the actual listing item object, I have to go with option 2/3, but now I've got redundant data in two places that conceivably won't be in sync (although practically speaking, in my app, because of paging/sorting I must necessarily re-poll for latest data from the API upon returning back to the listing) or an array of items that can contain items in two different object formats.
Both feel dirty, which leads me to believe I'm missing something obvious that redux is already equipped to handle. Suggestions? Additional options?
Can the user update multiple items at the same time? Redux has 3 important principles to respect. One of them is the single source of truth, which means all valid/true data must come from one place (store here) and every data element is stored exactly once. Having multiple time the same items in the store is bad, mainly because of sync problems like you said. Also having deep objects is not a good approach, you should keep your state flat. Since you're using react with redux, you have access to state, the app state with redux and the component state with react. The most common pattern with this kind of problem is to create a copy of the object that you are editing in the component state. If the user cancels the editing, it will be easy to revert it. If the user stops editing the item without saving, you app state won't be affected. Also, you will avoid polluting your app state with wrong or duplicated data. Let's say that in your list when a user clicks on an item, the item transform to a text input. The list item will be base on the redux store item and the text input will be base on a copy of the same item. This also applies to new objects. You can also validate your item before adding/updating it. Keep in mind that the most important part is to keep your store clean because you will base you displayed items on that, so fetching new items won't affect your list. Doing this will also help you updating the item in the store because you will only have to replace it when dispatching a save action.

Resources