Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor post threads to use react query #1851

Merged
merged 11 commits into from
Nov 9, 2023
Merged

Refactor post threads to use react query #1851

merged 11 commits into from
Nov 9, 2023

Conversation

pfrazee
Copy link
Collaborator

@pfrazee pfrazee commented Nov 9, 2023

This PR refactors the Post Thread code to use React Query (RQ).

In addition to refactoring the Post Thread, this PR introduces a "Post Shadow" cache which is used for optimistic updates that we want to keep synced across views (like, repost, and deletion state).

Some notes:

  • This PR is using a temporary useSession hook that lightly wraps the existing session manager.
  • There are some commented out TODOs in the component which have to do with the previous post cache. We don't want to get rid of that yet because it's a case we're still going to need to handle -- using posts from a feed to create a pre-fetch skeleton of threads.
  • There are a couple of components which I've temporarily replaced by adding a 2 to the filename, since there's still existing code depending on them.

import {RQKEY as POST_RQKEY, getCachedPost} from './post'
import {ThreadViewPreference} from '../models/ui/preferences'

export const RQKEY = (uri: string) => ['post-thread', uri]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not positive we'd really see an issue with this, but we might want to include the user did or something in this keyset so that switching users without a reload to clear memory never results in stale data being shown from cache.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed we'd drop all RQ cache on a session change

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to have our React Query Keys strongly typed in one place so we can refer to them for future updates. A very simple factory model should work: https://tkdodo.eu/blog/effective-react-query-keys#use-query-key-factories

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm +1 to this, nice to have a quick reference to what query keys are out there

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually @ansh does it change your mind any if we have separate query hooks for each query like Paul has here? I.e. usePostQuery instead of useQuery(RQKEY('uri'), () => {}). So finding all usages is pretty easy either way.

)
}

export function sortThreadSkeleton(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions are so clear 🥺 I love them

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah why dont you marry them

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would love a comment overview of how this function sorts the posts for future contributors:

// 1. If node is not a post return it unmodified, otherwise continue.
// 2. If the node has replies, start the sorting process.
// 3. Retrieve the cached post for the current node using its URI.
// 4. Sort the replies array in place using a custom comparator.
// 5. Inside the comparator, give priority to posts over non-posts.
// 6. Retrieve cached posts for both comparison nodes using their URIs.
// 7. If the cached post for 'a' is not found, it is moved down in the sort order.
// 8. If the cached post for 'b' is not found, it is moved up in the sort order.
// 9. Check if the author of post 'a' is the original poster (OP) and the same for post 'b'.
// 10. If both are by the OP, sort by the timestamp 'indexedAt' to get the oldest post.
// 11. If only 'a' is by the OP, it gets higher priority.
// 12. If only 'b' is by the OP, 'a' gets higher priority.
// 13. If user preference is to prioritize followed users, compare the following status of each post's author.
// 14. Sort by followed status, giving priority to followed user's posts.
// 15. If sorting by 'oldest', sort by 'indexedAt' to get the oldest post.
// 16. If sorting by 'newest', sort by 'indexedAt' in reverse to get the newest post.
// 17. If sorting by 'most-likes', compare like counts; in case of a tie, use 'indexedAt' to prioritize newer posts.
// 18. If sorting by 'random', return a random order by using Math.random.
// 19. Default to sorting by 'indexedAt' in reverse if no other conditions are met.
// 20. Recursively apply the sorting function to each reply.
// 21. Finally, return the sorted node.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a huge fan of describing code a second time in the comments -- only summarizing or explaining

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: conceptually I think I like @gaearon's distinction between state and "cache" i.e. a local cache of remote data that only changes on a server. In respect to that, I'm kinda inclined to say we should put queries in a src/data/* dir or something outside the state dir, but that's a very soft opinion. Remote cache/data is "state" in a way too. Idk!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

totally open to different directory structures here

} from '@tanstack/react-query'
import {useSession} from '../session'

export const RQKEY = (postUri: string) => ['post', postUri]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to have this in a typed key generating factory as previously mentioned

return res.data.posts[0]
}

throw new Error('No data')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this error propagated somewhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it becomes RQ error state, right?

})
return {likeCount}
},
onSuccess(data, variables) {
Copy link
Contributor

@ansh ansh Nov 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React Query v5 is out which simplifies the optimistic update workflow. We no longer have to manually update the cache every time:

https://medium.com/@stojanovic.nemanja71/optimistic-updates-in-tanstack-query-v5-dfbcbb124113

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies to other optimistic updates as well. Not adding the comment everywhere fyi.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The link above is how we should do things in most cases. In this case, the post shadow is synced across components and it's better to put in these handlers

isRefetching,
data: threadSkeleton,
} = usePostThreadQuery(resolvedUri)
const {data: rootPost} = usePostQuery(threadSkeleton?.uri)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love how the code is simplified!

Post hidden
</Text>
<Text type="md" style={[pal.text, s.mb10]}>
You have blocked the author or you have been blocked by the author.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please merge with main and add <Trans> here

<CenteredView>
<View style={[pal.view, pal.border, styles.notFoundContainer]}>
<Text type="title-lg" style={[pal.text, s.mb5]}>
Post hidden
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<Trans> here too

@pfrazee pfrazee marked this pull request as ready for review November 9, 2023 23:34
@pfrazee pfrazee merged commit fb4f570 into main Nov 9, 2023
4 checks passed
@pfrazee pfrazee deleted the rq-postthread branch November 9, 2023 23:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants