React Migration: Dynamic Nest API, course sidebar, breadcrumbs#6100
Merged
ekowidianto merged 76 commits intomasterfrom Jun 27, 2023
Merged
React Migration: Dynamic Nest API, course sidebar, breadcrumbs#6100ekowidianto merged 76 commits intomasterfrom
ekowidianto merged 76 commits intomasterfrom
Conversation
85d2fcc to
b934e58
Compare
ekowidianto
reviewed
Jun 14, 2023
Member
There was a problem hiding this comment.
Some initial comments below. I know it's still WIP and some things are not cleaned yet but I left some comments just as a reminder.
- Dont forget to remove existing rails sidebar and course slim files.
- Port the rest of PageHeader to Page as well?
- To replace relevant
<a />and<Link />(fromcomponents/core) to react-router'sLink.
2fac312 to
8662579
Compare
8662579 to
3988ead
Compare
f5c0fc3 to
22f0fc9
Compare
`active_course` sometimes appears in another page
ekowidianto
approved these changes
Jun 27, 2023
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the new course sidebar and breadcrumbs. There are some pages that were refactored and/or restyled to be consistent.
Closes #2542.
Remaining tasks
PageDynamic Nest API
React Router 6.11.x provides
NavLinkanduseMatchesthat can help us:The gold standard for this routing is Course Settings. However, other pages have rather unconventional routing systems. We are still transitioning from Rails' router to React Router, and not all pages are nested according to their routes (see Manage Users, Submissions, or Skills). This means using
NavLink'sisActivealone isn't enough, because:assessments/:id/*, the correct category on the sidebar won't be highlighted because the sidebar item's URLs are in the form ofassessments?category=*,students,By generating breadcrumbs in the front-end, we also lose the ability to generate dynamic breadcrumbs. Consider these routes:
assessmentsassessments?category=11's default tabassessments?category=1&tab=21's Tab2assessments/3/*3is keptassessments/submissionsassessments/skillsThe first 4 cases are the hardest. Unlike Rails, when we go to these routes, we don't have the luxury of traversing from
ApplicationControllerto the assessment's controller and gradually generate the crumbs. Also, in the client, we don't have access to the database, so there's no knowing the category of assessment3to generate the crumb (unless via GETs). Enter the Dynamic Nest API.Read the API documentation
Description
The Dynamic Nest API allows us to generate crumbs based, but not dependent on the route.
The Dynamic Nest API is NOT for generating breadcrumbs, rather defining dynamic nesting behaviours that defy the static nature of routes and their matches. The information it generates allows us to generate an overridable active route nest (problem 1 above) and a dynamic breadcrumb (problem 2 above).
It allows for an extensible way of defining custom logic for generating crumbs (matches) by leveraging React Router's handles as React Router traverses from
/to the target route. This is the equivalent to Rails' inside-out controller traversal (not to be confused with Rails' outside-in routing). This is the strategy that allows us to generate the correct crumbs whether the user is coming (a) from somewhere else in our app, or (b) directly via some URL.These handles can be as simple as a string or translation object, to as complex as async network calls with custom flags (for the use case of assessments above).
We thus define a handle as a nest-specific abstraction of information that the Dynamic Nest API will use to generate its matches, which are used to generate a breadcrumb and specify an active path.
Implementation
A handle in the Dynamic Nest API can be a:
nullorundefined(crumb will not be generated)Descriptor(object passed touseTranslation)DataHandle(this is the big brain moment)DataHandlefor rendering a dynamic breadcrumbA
DataHandleis a handle that produces a data for the API to generate crumbs from.matchandlocationare provided by React Router and exposed here for convenience. With these, you can accessparamsandloader's data (frommatch) or search params (fromlocation). It returnsHandleData, that could be aCrumbTitleorHandleRequest(this is the big brain moment)HandleRequestfor defining a dynamic nestHandleRequestis really the power of the Dynamic Nest API. Think ofHandleRequestas an object filled with configurations to "request" the API to generate crumbs according to our liking.HandleRequestis currently an object with 2 attributes:shouldRevalidateandgetData.getDatais a function that returns, directly orPromised, either aCrumbTitleorCrumbPath. ACrumbPathis a partial ofCrumbData(internal), the object passed directly to theCrumbcomponent that appears inBreadcrumbs. This way, in addition to just providing the custom crumb title (as you could withCrumbTitlebefore), you can provide:url?: string: a custom URL for the crumb,activePath?: boolean: the prefix that signifies the current active nest,pathname?: string: the identifier that the API uses to cache and invalidate rendered crumbs (advanced use only).useDynamicNest's rendering algorithmTo understand the use of
shouldRevalidate, one must first understand howuseDynamicNesthandles crumb generation (seebuildCrumbsData, later referred to as the builder).If a route has N nests, it will have N matches, and thus N handles will be collected by the builder. At worst, if these N handles make network calls each, then there will be N network calls, too. The API is designed to only make these calls when the builder handles them, and that they are done asynchronously. The API also handles race conditions.
Whenever the route changes,
useMatcheswill generate new set of matches thatuseDynamicNestwill use to render by sieving (my own term). It will go through the last crumbs and the new matches, and only build the new crumbs. The diff is done bypathnames. All this is done in one pass of N iterations.The case for
shouldRevalidateNow think the assessments case above (see the table above). What if we switch between tabs in an assessment category? What if we jump from a mission in category A tab A to category B tab B? The crumb for category-tab will not change, because the pathname is the same:
assessments/.useMatcheswon't even know we have jumped into a new nest (new category-tab), because all it knows is just theassessments/match. EntershouldRevalidate.shouldRevalidate: truenotifies the builder that for this particular match M, we want to keep it, but still additionally collect its handle, in case it may give us a new result. Hence, "revalidate". Note that withoutshouldRevalidate, kept matches/crumbs will not have their handles collected.This way, the user will not see the old category/tab's crumb disappear all the time, and if we indeed has jumped into a new nest, the new crumb data is built, and it just replaces that crumb. Neat, eh?
Finally, the
useDynamicNesthookThis hook internally is just a state management function. The heavy-lifting is done by the builder. It returns an object of 3 attributes, each which is self-explanatory at this point.
loadingwill betrueif the number of expected final crumbs > number of crumbs remaining in state.loadingfalsedoes NOT necessarily mean there are no network requests or pending handle collection. It is designed this way because whenloadingistrue, we render a loading animation at the end of the breadcrumb.