-
Notifications
You must be signed in to change notification settings - Fork 26
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
✨ Allow adding all lists of a user to workspace #220
Conversation
foysalit
commented
Oct 17, 2024
await addItemsToWorkspace(nextLists.data.lists.map((f) => f.uri)) | ||
cursor = nextLists.data.cursor | ||
// if the modal is closed, that means the user decided not to add any more user to workspace | ||
} while (cursor && isConfirmationOpen) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the nature of how react works, this condition will not actually cancel the loop as isConfirmationOpen
is a const that will never become false.
For this to work you'll probably need to rely on a ref & AbortController.
const runController = ref<AbortController | null>(null)
const isWorking = abortRef.current != null
const startWorking = async () => {
// Get a reference to the previous run controller before we replace it with our own
const prevController = runController.current
const controller = new AbortController()
runController.current = controller
// Abort previous run, if any
prevController?.abort()
const { signal } = controller
try {
do {
await someAsyncStuff({
signal
})
} while (!signal.aborted)
if (signal.aborted) {
// make sure the catch block is triggered
throw signal.reason
}
} catch (err) {
if (signal.aborted) {
toast.info('this action was aborted')
} else {
toast.error(`Something went wrong: ${(e as Error).message}`)
}
} finally {
// Prevent re-use (for good measure)
controller.abort()
// If this run is still the currently active one
if (runController.current === controller) {
// Cleanup reference
runController.current = null
// Close the modal
setIsConfirmationOpen(false)
}
}
}
const abortCurrentRun = () => {
// No need to call setIsConfirmationOpen here as this will be done through startWorking's finally block
runController.current?.abort()
}
// Abort the current run when the component is unmounted (e.g. due to navigation)
useEffect(() => {
return () => runController.current?.abort()
}, [runController])
PS: If this feels too manual, the mutation lib probably already does all of the above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah I knew it wasn't optimal but it does kill the requests if other conditions cause a re-render.
we use this pattern in a few places so I cleaned up all usage of this pattern to use abort controllers. thank you for pointing it out!
@@ -112,27 +122,42 @@ function useSearchResultsQuery(q: string) { | |||
return | |||
} | |||
setIsAdding(true) | |||
const newAbortController = new AbortController() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For good measure, we should ensure that only one run is possible at any given time.
const newAbortController = new AbortController() | |
// allready running | |
if (abortController.current) return | |
const newAbortController = new AbortController() |
or
const newAbortController = new AbortController() | |
// user cancelled by confirming again | |
abortController.current?.abort('user-cancelled') | |
const newAbortController = new AbortController() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm... not sure how this case could happen since every interaction aborts the controller after it is instantiated. especially with the abort on unmount. I will give it another look when refactoring these into a hook.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It shouldn't happen, but only because the isAdding
UI prevents it to. So if someone else breaks that code (eg. by changing the UI), it might end-up happening in parallel. Adding this is just a safety measure against future changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes all look very similar. I wonder if there is a way to avoid the code duplication by making some kind of hook ? Looks good otherwise.
@@ -90,6 +99,7 @@ const getRepos = | |||
} | |||
|
|||
function useSearchResultsQuery(q: string) { | |||
const abortController = useRef<AbortController | null>(null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to cancel the current run if the component is un-mounted (same thing in other places)
const abortController = useRef<AbortController | null>(null) | |
const abortController = useRef<AbortController | null>(null) | |
useEffect(() => { | |
// User cancelled by closing this view (navigation, other?) | |
return () => abortController.current?.abord('user-cancelled') | |
}, []) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think i was avoiding this earlier on so that folks can run one of these and leave it running in the background while they browse and do other things. but in hindsight, that may be a bit too risky since it will make it very easy for people to leave something like this running for 20K followers or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed on the refactoring into a hook recommendation. I'm adding a ticket for that and don't wanna bite that off in this PR. let me get this out and will handle the refactoring in a future, separate PR.