fix: stop the course outline scrolling back while you work in it - #3213
bradenmacdonald merged 2 commits into
Conversation
|
Thanks for the pull request, @ihor-romaniuk! This repository is currently maintained by Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review. 🔘 Get product approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere can I find more information?If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources: When can I expect my changes to be merged?Our goal is to get community contributions seen and reviewed as efficiently as possible. However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
9f24e83 to
c2ec4a7
Compare
Opening a course outline through a link that points at one of its blocks left the page snapping back to that block on almost every interaction — opening a section menu was enough. The block stays highlighted for as long as the link is in the address bar, and the outline reads that as "scroll to it" again each time it re-renders. The global-state hook handed out a new pair of callbacks on every render, and the cards that scroll to the highlighted block list one of those callbacks among the dependencies of the effect that scrolls. The callbacks now keep their identity, so the effect runs when the highlighted block changes rather than on every render. Scrolling to the block on arrival is unchanged.
c2ec4a7 to
344320b
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3213 +/- ##
==========================================
+ Coverage 95.92% 96.03% +0.10%
==========================================
Files 1397 1407 +10
Lines 33581 34288 +707
Branches 7947 7882 -65
==========================================
+ Hits 32214 32929 +715
- Misses 1308 1318 +10
+ Partials 59 41 -18 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
I believe this has already been fixed on master. I just tested it now and could not reproduce the issue.
I don't think this is correct, because Nevertheless, it may still be worth merging this PR. The However, I'm wondering why we even have @navinkarkera what do you think? could you please review this? |
navinkarkera
left a comment
There was a problem hiding this comment.
Even I was not able to reproduce the issue.
But I agree with @bradenmacdonald , it is still worth merging.
However, I'm wondering why we even have createGlobalState at all - it's a bit of a hack to share state among components using React Query, but it's not using any of the query lifecycle or fetching features (staleness, refetching, invalidation). It would be cleaner to use a small context or perhaps useSyncExternalStore.
Yes, I added it while refactoring outline page and simplifying storage, it felt cleaner to have react query manage the state but I agree that it would be better to use a context (don't remember why I did not do it).
There was a problem hiding this comment.
@ihor-romaniuk Can you please just make this change to the comment here, and then I'll merge this fix?
--- a/src/data/apiHooks.ts
+++ b/src/data/apiHooks.ts
@@ -152,7 +152,12 @@ export const useCourseDetails = (courseId: string) => {
};
/**
- * Create a global state function for a query.
+ * Create a global state function.
+ *
+ * This is sort of a hack for sharing state between components using React Query
+ * without actually loading data from an API. Instead of using this, please use
+ * a small context or `useSyncExternalStore`, and feel free to remove this once
+ * we've replaced the existing usage of it on the outline page.
*/
export function createGlobalState<T>(
queryKeyFn: (queryKeyArgs?: any) => QueryKey,Wording from the review: the helper is a hack for sharing state through React Query, and callers should reach for a context or useSyncExternalStore.
|
@bradenmacdonald Thanks for your review and JSDOC was updated. |
…kport #3213) (#3214) * fix: stop the course outline scrolling back while you work in it Opening a course outline through a link that points at one of its blocks left the page snapping back to that block on almost every interaction — opening a section menu was enough. The block stays highlighted for as long as the link is in the address bar, and the outline reads that as "scroll to it" again each time it re-renders. The global-state hook handed out a new pair of callbacks on every render, and the cards that scroll to the highlighted block list one of those callbacks among the dependencies of the effect that scrolls. The callbacks now keep their identity, so the effect runs when the highlighted block changes rather than on every render. Scrolling to the block on arrival is unchanged. * docs: say what createGlobalState is and what to use instead Wording from the review on the master PR, carried over so the two branches stay identical.
Description
Open a course outline through a link that points at one of its blocks — the kind Studio search and "view in outline" produce, with
?show=<block>in the address — and the page starts snapping back to that block. Opening the menu on any section is enough to be thrown back to it, which makes the outline hard to work in at all. The block stays highlighted for as long as the link is in the address bar, and the outline reads that as "scroll to it" every time it re-renders.The outline now scrolls to the highlighted block when you arrive and then leaves the page alone.
Implementation notes
createGlobalStateinsrc/data/apiHooks.tsreturnedsetDataandresetDataas plain functions declared in the hook body, so both got a new identity on every render.SectionCard,SubsectionCardandUnitCardlistresetScrollStateamong the dependencies of the effect that callsscrollToElement, so that effect ran on every render. While?show=names the card, its guardisScrolledToElementstays true, so every render scrolled. The effect also callsresetScrollState(), which invalidates the query and causes another render — hence repeated jumps rather than one.useQuery'srefetchdoes.queryKeyis memoised onqueryKeyArgsso the callbacks can depend on it honestly, without a ref.createGlobalState.Screenshot/Video
Before
before.mov
After
after.mov
Testing
Preconditions: a course with several sections, a staff user.
<AUTHORING>/course/<courseId>?show=<sectionId>.?show=and repeat.Verified locally
?show=<section>, scrolled to 6000px, section menu openedaria-expandedflips and the dropdown rendersjest src/course-outlinejest src/data/apiHooks.test.tsxtsc,dprint,oxlint